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
dbg_print(const char * fmt,...)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
smc_send_local_cmd(int fd,sc_reqmsg_t * req_pkt,sc_rspmsg_t * rsp_pkt,int poll_time)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
get_geo_addr(uint8_t * geo_addr)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
checksum(uint8_t buf[],int start,int end)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
smc_send_ipmi_message(int fd,sc_reqmsg_t * req_pkt,sc_rspmsg_t * rsp_pkt,int poll_time)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
smc_init_ipmi_msg(sc_reqmsg_t * req_msg,uint8_t cmd,uint8_t msg_id,uint8_t msg_data_size,uint8_t * msg_data_buf,int8_t seq_num,int ipmb_addr,smc_netfn_t netfn,smc_lun_t lun)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
smc_init_smc_msg(sc_reqmsg_t * req_msg,smc_app_command_t cmd,uint8_t msg_id,uint8_t msg_data_size)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
smc_send_msg(int fd,sc_reqmsg_t * req_msg,sc_rspmsg_t * rsp_msg,int poll_time)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