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 } 477