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  * This is utility library that provides APIs to interact with SMC driver
  31  */
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <sys/stat.h>
  36 #include <fcntl.h>
  37 #include <poll.h>
  38 #include <assert.h>
  39 #include <unistd.h>
  40 #include <string.h>
  41 #include <strings.h>
  42 #include <stdarg.h>
  43 #include <stropts.h>
  44 #include <syslog.h>
  45 #include "smclib.h"
  46 
  47 static int debug_on = 0;
  48 
  49 /* Error messages */
  50 #define SMC_ERRMSG_OPEN         "SMC open failed, cmd = %x\n"
  51 #define SMC_ERRMSG_WRITE        "SMC write failed, cmd = %x\n"
  52 #define SMC_ERRMSG_POLLTIMEOUT  "SMC poll timed out, cmd = %x\n"
  53 #define SMC_ERRMSG_POLLFAILED   "SMC poll failed, cmd = %x\n"
  54 #define SMC_ERRMSG_POLL_T       "SMC poll timed out, dest = %x\n"
  55 #define SMC_ERRMSG_POLL_F       "SMC poll failed, dest = %x\n"
  56 #define SMC_ERRMSG_READ         "SMC read response failed, cmd = %x\n"
  57 #define SMC_ERRMSG_ERROR        "SMC error, cc = %d, msg_id = %x\n"
  58 #define SMC_ERRMSG_SETATTR      "SMC setting read attribute failed\n"
  59 #define SMC_ERRMSG_GET_SEQN     "SMC error in getting seqn for %x\n"
  60 #define SMC_ERRMSG_IPMI_ERR     "SMC IPMI invalid cc:%x, dest = %x\n"
  61 #define SMC_ERRMSG_GET_GEO      "SMC get GeoAddr failed\n"
  62 
  63 /* Macros */
  64 #define REQ_SA(_X)      (((_X) < 10) ? (0xb0 + 2 * ((_X) - 1)) :\
  65                                         (0xb0 + 2 * (_X)))
  66 #define LUN_BITMASK             0x03    /* last two bits */
  67 #define RESPONSE_MSG            0x01    /* last bit */
  68 
  69 #define SMC_LOCAL_SEQ_NO        10
  70 #define SMC_POLL_TIME           1000    /* 1 sec */
  71 #define NORMAL_COMPLETION_CODE  0
  72 #define IPMI_MSG_CHANNEL_0      0x0
  73 #define IPMI_REQ_HDR_LEN        0x8     /* includes command & data checksum */
  74 #define IPMI_RSP_HDR_LEN        0x8
  75 #define SMC_NETFN_SEQ_OFFSET    5
  76 #define SMC_CMD_OFFSET          6
  77 
  78 #define SMC_NODE                ("/dev/ctsmc")
  79 #define DEFAULT_FD                      -1
  80 #define DEFAULT_SEQN                    128
  81 
  82 /*
  83  * IPMI packet header
  84  */
  85 typedef struct {
  86         uint8_t channel_no;             /* channel num */
  87         uint8_t rs_addr;                /* dest addr */
  88         uint8_t netfn_lun;              /* netfn and lun */
  89         uint8_t checksum;               /* checksum for dest and netfn_lun */
  90         uint8_t rq_addr;                /* sender addr */
  91         uint8_t seq_num;                /* sequence number */
  92         uint8_t cmd;                    /* ipmi cmd */
  93 } smc_ipmi_header_t;
  94 
  95 /*
  96  * debug printf
  97  */
  98 static void
  99 dbg_print(const char *fmt, ...)
 100 {
 101         if (debug_on > 0) {
 102                 va_list ap;
 103                 va_start(ap, fmt);
 104                 (void) vprintf(fmt, ap);
 105                 va_end(ap);
 106         }
 107 }
 108 
 109 /*
 110  * send a local command to SMC
 111  */
 112 static smc_errno_t
 113 smc_send_local_cmd(int fd, sc_reqmsg_t *req_pkt, sc_rspmsg_t *rsp_pkt,
 114         int poll_time)
 115 {
 116         int     poll_rc;
 117         struct pollfd   poll_fds[1];
 118 
 119         poll_fds[0].fd          = fd;
 120         poll_fds[0].events      = POLLIN|POLLPRI;
 121         poll_fds[0].revents     = 0;
 122 
 123         /* send the command to SMC */
 124         if (write(fd, req_pkt, SC_SEND_HEADER + SC_MSG_LEN(req_pkt)) < 0) {
 125                 dbg_print(SMC_ERRMSG_WRITE, SC_MSG_CMD(req_pkt));
 126                 return (SMC_REQ_FAILURE);
 127         }
 128 
 129         poll_rc = poll(poll_fds, 1, poll_time);
 130         if (poll_rc == 0) {
 131                 dbg_print(SMC_ERRMSG_POLLTIMEOUT, SC_MSG_CMD(req_pkt));
 132                 return (SMC_ACK_FAILURE);
 133         } else if (poll_rc == -1) {
 134                 dbg_print(SMC_ERRMSG_POLLFAILED, SC_MSG_CMD(req_pkt));
 135                 return (SMC_ACK_FAILURE);
 136         }
 137 
 138         /* read the response from SMC */
 139         if (read(fd, rsp_pkt, SC_MSG_MAX_SIZE) == -1) {
 140                 dbg_print(SMC_ERRMSG_READ, SC_MSG_CMD(req_pkt));
 141                 return (SMC_ACK_FAILURE);
 142         }
 143 
 144         /* check if response is valid */
 145         if (SC_MSG_ID(rsp_pkt) != SC_MSG_ID(req_pkt)) {
 146                 dbg_print(SMC_ERRMSG_ERROR, SC_MSG_CC(rsp_pkt),
 147                         SC_MSG_ID(rsp_pkt));
 148                 return (SMC_INVALID_SEQ);
 149         }
 150 
 151         if (SC_MSG_CC(rsp_pkt) != 0) {
 152                 return (SMC_FAILURE);
 153         }
 154 
 155         return (SMC_SUCCESS);
 156 }
 157 
 158 /*
 159  * get_geo_addr -- returns the geographical address of a CPU board
 160  */
 161 static int
 162 get_geo_addr(uint8_t *geo_addr)
 163 {
 164         int     fd, rc;
 165         sc_reqmsg_t     req_pkt;
 166         sc_rspmsg_t     rsp_pkt;
 167 
 168         if ((fd = open(SMC_NODE, O_RDWR)) < 0) {
 169                 dbg_print(SMC_ERRMSG_OPEN,
 170                         SMC_GET_GEOGRAPHICAL_ADDRESS);
 171                 return (SMC_FAILURE);
 172         }
 173 
 174         SC_MSG_CMD(&req_pkt) = SMC_GET_GEOGRAPHICAL_ADDRESS;
 175         SC_MSG_LEN(&req_pkt) = 0;
 176         SC_MSG_ID(&req_pkt)  =  SMC_LOCAL_SEQ_NO;
 177 
 178         /* no request data */
 179         if ((rc = smc_send_local_cmd(fd, &req_pkt, &rsp_pkt,
 180                 SMC_POLL_TIME)) != SMC_SUCCESS) {
 181                 (void) close(fd);
 182                 return (rc);
 183         }
 184 
 185         *geo_addr = rsp_pkt.data[0];
 186         (void) close(fd);
 187         return (SMC_SUCCESS);
 188 }
 189 
 190 /*
 191  * checksum - returns a 2-complement check sum
 192  */
 193 static uint8_t
 194 checksum(uint8_t buf[], int start, int end)
 195 {
 196         int     i;
 197         uint8_t sum = 0x0;
 198 
 199         for (i = start; i <= end; i++) {
 200                 sum += buf[i];
 201         }
 202         sum = ~sum + 1;
 203         return (sum);
 204 }
 205 
 206 /*
 207  * func to send IPMI messages
 208  */
 209 static smc_errno_t
 210 smc_send_ipmi_message(int fd, sc_reqmsg_t *req_pkt, sc_rspmsg_t *rsp_pkt,
 211         int poll_time)
 212 {
 213         int             result, nbytes, i = 0;
 214         struct          pollfd fds;
 215         uint8_t         cc, netfn;
 216         boolean_t       is_response = B_FALSE;
 217         char data[SC_MSG_MAX_SIZE], *p;
 218 
 219         if (debug_on) {
 220                 bzero(data, SC_MSG_MAX_SIZE);
 221                 p = data;
 222                 for (i = 0; i < SC_MSG_LEN(req_pkt); i++) {
 223                         (void) sprintf(p, "%02x ", req_pkt->data[i]);
 224                         p = data + strlen(data);
 225                 }
 226                 p = data;
 227                 syslog(LOG_ERR, "REQ> %s", p);
 228         }
 229 
 230         netfn = req_pkt->data[2] >> 2;
 231         if (netfn & RESPONSE_MSG) {
 232                 is_response = B_TRUE;
 233         }
 234 
 235         if ((nbytes = write(fd, (char *)req_pkt, SC_SEND_HEADER  +
 236                 SC_MSG_LEN(req_pkt))) < 0) {
 237                 dbg_print(SMC_ERRMSG_WRITE, SMC_SEND_MESSAGE);
 238                 return (SMC_REQ_FAILURE);
 239         }
 240 
 241         if ((nbytes = read(fd, (char *)rsp_pkt, SC_MSG_MAX_SIZE)) < 0) {
 242                 dbg_print(SMC_ERRMSG_READ, SMC_SEND_MESSAGE);
 243                 return (SMC_ACK_FAILURE);
 244         }
 245 
 246         if (SC_MSG_CC(rsp_pkt) != 0) {
 247                 dbg_print(SMC_ERRMSG_ERROR, SC_MSG_CC(rsp_pkt),
 248                         SC_MSG_ID(rsp_pkt));
 249                 return (SMC_ACK_FAILURE);
 250         }
 251 
 252         if (is_response) {      /* need not wait for response */
 253                 return (SMC_SUCCESS);
 254         }
 255 
 256         fds.fd  = fd;
 257         fds.events = POLLIN | POLLPRI;
 258         fds.revents = 0;
 259         result = poll(&fds, 1, poll_time);
 260 
 261         if (result == 0) {
 262                 dbg_print(SMC_ERRMSG_POLL_T, req_pkt->data[1]);
 263                 return (SMC_RSP_TIMEOUT);
 264         } else if (result < 0) {
 265                 dbg_print(SMC_ERRMSG_POLL_F, req_pkt->data[1]);
 266                 return (SMC_RSP_ERROR);
 267         }
 268 
 269         nbytes = read(fd, rsp_pkt, SC_MSG_MAX_SIZE);
 270         if (nbytes < 0) {
 271                 dbg_print(SMC_ERRMSG_READ, SMC_SEND_MESSAGE);
 272                 return (SMC_RSP_ERROR);
 273         }
 274 
 275         if (debug_on) {
 276                 bzero(data, SC_MSG_MAX_SIZE);
 277                 p = data;
 278                 for (i = 0; i < nbytes; i++) {
 279                         (void) sprintf(p, "%02x ", rsp_pkt->data[i]);
 280                         p = data + strlen(data);
 281                 }
 282                 p = data;
 283                 syslog(LOG_DEBUG, "RES> %s, seq = %x, cmd = %x, len = %x,"
 284                         "cc = %x", p, SC_MSG_ID(rsp_pkt), SC_MSG_CMD(rsp_pkt),
 285                                 SC_MSG_LEN(rsp_pkt), SC_MSG_CC(rsp_pkt));
 286         }
 287 
 288         if (SC_MSG_CC(rsp_pkt) != 0) {
 289                 dbg_print(SMC_ERRMSG_IPMI_ERR, rsp_pkt->hdr.cc,
 290                         req_pkt->data[SMC_CMD_OFFSET]);
 291                 return (SMC_RSP_ERROR);
 292         }
 293 
 294         if (req_pkt->data[SMC_NETFN_SEQ_OFFSET] !=
 295                 rsp_pkt->data[SMC_NETFN_SEQ_OFFSET]) {
 296                 dbg_print("SMC: Invalid sequence number in"
 297                 " IPMI Response (sent %x, received %x)\n",
 298                         req_pkt->data[5], rsp_pkt->data[SMC_NETFN_SEQ_OFFSET]);
 299         }
 300 
 301         if ((cc = rsp_pkt->data[IPMI_RSP_HDR_LEN-1]) != 0) {
 302                 dbg_print("SMC:IPMI response completion "
 303                         "error %x, command = %x\n",
 304                                 cc, req_pkt->data[SMC_CMD_OFFSET]);
 305         }
 306         return (SMC_SUCCESS);
 307 }
 308 
 309 /*
 310  * Initializes the IPMI request packet
 311  */
 312 smc_errno_t
 313 smc_init_ipmi_msg(sc_reqmsg_t *req_msg, uint8_t cmd, uint8_t msg_id,
 314         uint8_t msg_data_size, uint8_t *msg_data_buf, int8_t seq_num,
 315         int ipmb_addr, smc_netfn_t netfn, smc_lun_t lun)
 316 {
 317         static uint8_t  geo_addr = 0;
 318         smc_ipmi_header_t ipmi_header;
 319         uint8_t data[2];
 320         if (msg_data_size > 0) {
 321                 if ((msg_data_size > (SC_SEND_DSIZE - IPMI_REQ_HDR_LEN)) ||
 322                         (msg_data_buf == NULL)) {
 323                         return (SMC_FAILURE);
 324                 }
 325         }
 326 
 327         /* get the geo addr for first time */
 328         if (geo_addr == 0) {
 329                 if (get_geo_addr(&geo_addr) != SMC_SUCCESS) {
 330                         dbg_print(SMC_ERRMSG_GET_GEO);
 331                         return (SMC_FAILURE);
 332                 }
 333         }
 334 
 335         SC_MSG_CMD(req_msg) = SMC_SEND_MESSAGE;
 336         SC_MSG_ID(req_msg) = msg_id;
 337         SC_MSG_LEN(req_msg) = IPMI_REQ_HDR_LEN + msg_data_size;
 338         ipmi_header.channel_no = IPMI_MSG_CHANNEL_0;
 339         ipmi_header.rs_addr = data[0] = ipmb_addr;
 340         ipmi_header.netfn_lun = data[1] = (netfn << 2) | lun;
 341         ipmi_header.checksum = checksum(data, 0, 1);
 342         ipmi_header.rq_addr = REQ_SA(geo_addr);
 343         ipmi_header.cmd = cmd;
 344         if (seq_num >= 0 && seq_num < 64) {
 345                 ipmi_header.seq_num = (seq_num << 2) | SMC_SMS_LUN;
 346         } else {
 347                 ipmi_header.seq_num = DEFAULT_SEQN;
 348         }
 349 
 350         /* copy the header */
 351         (void) bcopy((void *)&ipmi_header, SC_MSG_DATA(req_msg),
 352                 sizeof (ipmi_header));
 353 
 354         /* copy the msg data into request packet */
 355         (void) bcopy((void *)msg_data_buf, (void *)((uchar_t *)req_msg->data +
 356                 (IPMI_REQ_HDR_LEN - 1)), msg_data_size);
 357         return (SMC_SUCCESS);
 358 }
 359 
 360 /*
 361  * Initialize a SMC packet
 362  */
 363 smc_errno_t
 364 smc_init_smc_msg(sc_reqmsg_t *req_msg, smc_app_command_t cmd,
 365         uint8_t msg_id, uint8_t msg_data_size)
 366 {
 367         if (msg_data_size > SC_SEND_DSIZE) {
 368                 return (SMC_FAILURE);
 369         }
 370 
 371         /* fill the packet */
 372         SC_MSG_CMD(req_msg) = cmd;
 373         SC_MSG_LEN(req_msg) = msg_data_size;
 374         SC_MSG_ID(req_msg) = msg_id;
 375         return (SMC_SUCCESS);
 376 }
 377 
 378 /*
 379  * Sends SMC(local) and IPMI messages
 380  */
 381 smc_errno_t
 382 smc_send_msg(int fd, sc_reqmsg_t *req_msg, sc_rspmsg_t *rsp_msg,
 383         int poll_time)
 384 {
 385         int rc = SMC_SUCCESS;
 386         uint8_t dsize, dest;
 387         boolean_t close_fd = B_FALSE;
 388         boolean_t free_seqn = B_FALSE;
 389         struct strioctl scioc;
 390         sc_seqdesc_t smc_seq;
 391         int8_t seq_no;
 392 
 393         if (req_msg == NULL || rsp_msg == NULL) {
 394                 return (SMC_FAILURE);
 395         }
 396 
 397         if (fd <  0) {
 398                 close_fd = B_TRUE;
 399                 if ((fd = open(SMC_NODE, O_RDWR)) < 0) {
 400                         dbg_print(SMC_ERRMSG_OPEN,
 401                                 SC_MSG_CMD(req_msg));
 402                         return (SMC_FAILURE);
 403                 }
 404         }
 405 
 406         if (ioctl(fd, I_SRDOPT, RMSGD) < 0) {
 407                 dbg_print(SMC_ERRMSG_SETATTR);
 408                 if (close_fd)
 409                         (void) close(fd);
 410                 return (SMC_FAILURE);
 411         }
 412 
 413         if (SC_MSG_CMD(req_msg) != SMC_SEND_MESSAGE) {
 414                 rc = smc_send_local_cmd(fd, req_msg, rsp_msg, poll_time);
 415                 if (close_fd) {
 416                         (void) close(fd);
 417                 }
 418                 return (rc);
 419         }
 420 
 421         /* This is an IPMI message */
 422         dsize = SC_MSG_LEN(req_msg) - IPMI_REQ_HDR_LEN;
 423         if (dsize > (SC_SEND_DSIZE - IPMI_REQ_HDR_LEN)) {
 424                 if (close_fd) {
 425                         (void) close(fd);
 426                 }
 427                 return (SMC_FAILURE);
 428         }
 429 
 430         /* check if sequence num is valid or not */
 431         if (req_msg->data[SMC_NETFN_SEQ_OFFSET] == DEFAULT_SEQN) {
 432                 free_seqn = B_TRUE;
 433                 bzero(&smc_seq, sizeof (sc_seqdesc_t));
 434                 dest = smc_seq.d_addr  = req_msg->data[1]; /* dest */
 435                 smc_seq.n_seqn = 1;
 436                 smc_seq.seq_numbers[0] = 0;
 437                 scioc.ic_cmd = SCIOC_RESERVE_SEQN;
 438                 scioc.ic_timout = 0;
 439                 scioc.ic_len = sizeof (sc_seqdesc_t);
 440                 scioc.ic_dp = (char *)&smc_seq;
 441                 if (ioctl(fd, I_STR, &scioc)  < 0) {
 442                         dbg_print(SMC_ERRMSG_GET_SEQN, dest);
 443                         if (close_fd) {
 444                                 (void) close(fd);
 445                         }
 446                         return (SMC_FAILURE);
 447                 }
 448                 seq_no = smc_seq.seq_numbers[0];
 449                 req_msg->data[SMC_NETFN_SEQ_OFFSET] =
 450                         (seq_no << 2) | SMC_SMS_LUN;
 451         }
 452 
 453         req_msg->data[(IPMI_REQ_HDR_LEN-1)+dsize] =
 454                 checksum(req_msg->data, 4, (IPMI_REQ_HDR_LEN-2)+dsize);
 455 
 456         rc = smc_send_ipmi_message(fd, req_msg, rsp_msg, poll_time);
 457 
 458         if (free_seqn) {        /* free seqn if library reserved it */
 459                 smc_seq.d_addr = dest;
 460                 smc_seq.n_seqn  = 1;
 461                 smc_seq.seq_numbers[0] = seq_no;
 462                 scioc.ic_cmd = SCIOC_FREE_SEQN;
 463                 scioc.ic_timout = 0;
 464                 scioc.ic_len = sizeof (sc_seqdesc_t);
 465                 scioc.ic_dp = (char *)&smc_seq;
 466                 if (ioctl(fd, I_STR, &scioc) < 0) {
 467                         dbg_print("SMC:Error in releasing sequence "
 468                                         "number\n");
 469                         rc = SMC_FAILURE;
 470                 }
 471         }
 472         if (close_fd) {
 473                 (void) close(fd);
 474         }
 475         return (rc);
 476 }