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
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.
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]
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * This is utility library that provides APIs to interact with SMC driver
47 static int debug_on
= 0;
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"
64 #define REQ_SA(_X) (((_X) < 10) ? (0xb0 + 2 * ((_X) - 1)) :\
66 #define LUN_BITMASK 0x03 /* last two bits */
67 #define RESPONSE_MSG 0x01 /* last bit */
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
78 #define SMC_NODE ("/dev/ctsmc")
80 #define DEFAULT_SEQN 128
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 */
99 dbg_print(const char *fmt
, ...)
104 (void) vprintf(fmt
, ap
);
110 * send a local command to SMC
113 smc_send_local_cmd(int fd
, sc_reqmsg_t
*req_pkt
, sc_rspmsg_t
*rsp_pkt
,
117 struct pollfd poll_fds
[1];
120 poll_fds
[0].events
= POLLIN
|POLLPRI
;
121 poll_fds
[0].revents
= 0;
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
);
129 poll_rc
= poll(poll_fds
, 1, poll_time
);
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
);
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
);
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
),
148 return (SMC_INVALID_SEQ
);
151 if (SC_MSG_CC(rsp_pkt
) != 0) {
152 return (SMC_FAILURE
);
155 return (SMC_SUCCESS
);
159 * get_geo_addr -- returns the geographical address of a CPU board
162 get_geo_addr(uint8_t *geo_addr
)
168 if ((fd
= open(SMC_NODE
, O_RDWR
)) < 0) {
169 dbg_print(SMC_ERRMSG_OPEN
,
170 SMC_GET_GEOGRAPHICAL_ADDRESS
);
171 return (SMC_FAILURE
);
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
;
178 /* no request data */
179 if ((rc
= smc_send_local_cmd(fd
, &req_pkt
, &rsp_pkt
,
180 SMC_POLL_TIME
)) != SMC_SUCCESS
) {
185 *geo_addr
= rsp_pkt
.data
[0];
187 return (SMC_SUCCESS
);
191 * checksum - returns a 2-complement check sum
194 checksum(uint8_t buf
[], int start
, int end
)
199 for (i
= start
; i
<= end
; i
++) {
207 * func to send IPMI messages
210 smc_send_ipmi_message(int fd
, sc_reqmsg_t
*req_pkt
, sc_rspmsg_t
*rsp_pkt
,
213 int result
, nbytes
, i
= 0;
216 boolean_t is_response
= B_FALSE
;
217 char data
[SC_MSG_MAX_SIZE
], *p
;
220 bzero(data
, SC_MSG_MAX_SIZE
);
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
);
227 syslog(LOG_ERR
, "REQ> %s", p
);
230 netfn
= req_pkt
->data
[2] >> 2;
231 if (netfn
& RESPONSE_MSG
) {
232 is_response
= B_TRUE
;
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
);
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
);
246 if (SC_MSG_CC(rsp_pkt
) != 0) {
247 dbg_print(SMC_ERRMSG_ERROR
, SC_MSG_CC(rsp_pkt
),
249 return (SMC_ACK_FAILURE
);
252 if (is_response
) { /* need not wait for response */
253 return (SMC_SUCCESS
);
257 fds
.events
= POLLIN
| POLLPRI
;
259 result
= poll(&fds
, 1, poll_time
);
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
);
269 nbytes
= read(fd
, rsp_pkt
, SC_MSG_MAX_SIZE
);
271 dbg_print(SMC_ERRMSG_READ
, SMC_SEND_MESSAGE
);
272 return (SMC_RSP_ERROR
);
276 bzero(data
, SC_MSG_MAX_SIZE
);
278 for (i
= 0; i
< nbytes
; i
++) {
279 (void) sprintf(p
, "%02x ", rsp_pkt
->data
[i
]);
280 p
= data
+ strlen(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
));
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
);
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
]);
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
]);
306 return (SMC_SUCCESS
);
310 * Initializes the IPMI request packet
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
)
317 static uint8_t geo_addr
= 0;
318 smc_ipmi_header_t ipmi_header
;
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
);
327 /* get the geo addr for first time */
329 if (get_geo_addr(&geo_addr
) != SMC_SUCCESS
) {
330 dbg_print(SMC_ERRMSG_GET_GEO
);
331 return (SMC_FAILURE
);
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
;
347 ipmi_header
.seq_num
= DEFAULT_SEQN
;
350 /* copy the header */
351 (void) bcopy((void *)&ipmi_header
, SC_MSG_DATA(req_msg
),
352 sizeof (ipmi_header
));
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
);
361 * Initialize a SMC packet
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
)
367 if (msg_data_size
> SC_SEND_DSIZE
) {
368 return (SMC_FAILURE
);
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
);
379 * Sends SMC(local) and IPMI messages
382 smc_send_msg(int fd
, sc_reqmsg_t
*req_msg
, sc_rspmsg_t
*rsp_msg
,
385 int rc
= SMC_SUCCESS
;
387 boolean_t close_fd
= B_FALSE
;
388 boolean_t free_seqn
= B_FALSE
;
389 struct strioctl scioc
;
390 sc_seqdesc_t smc_seq
;
393 if (req_msg
== NULL
|| rsp_msg
== NULL
) {
394 return (SMC_FAILURE
);
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
);
406 if (ioctl(fd
, I_SRDOPT
, RMSGD
) < 0) {
407 dbg_print(SMC_ERRMSG_SETATTR
);
410 return (SMC_FAILURE
);
413 if (SC_MSG_CMD(req_msg
) != SMC_SEND_MESSAGE
) {
414 rc
= smc_send_local_cmd(fd
, req_msg
, rsp_msg
, poll_time
);
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
)) {
427 return (SMC_FAILURE
);
430 /* check if sequence num is valid or not */
431 if (req_msg
->data
[SMC_NETFN_SEQ_OFFSET
] == DEFAULT_SEQN
) {
433 bzero(&smc_seq
, sizeof (sc_seqdesc_t
));
434 dest
= smc_seq
.d_addr
= req_msg
->data
[1]; /* dest */
436 smc_seq
.seq_numbers
[0] = 0;
437 scioc
.ic_cmd
= SCIOC_RESERVE_SEQN
;
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
);
446 return (SMC_FAILURE
);
448 seq_no
= smc_seq
.seq_numbers
[0];
449 req_msg
->data
[SMC_NETFN_SEQ_OFFSET
] =
450 (seq_no
<< 2) | SMC_SMS_LUN
;
453 req_msg
->data
[(IPMI_REQ_HDR_LEN
-1)+dsize
] =
454 checksum(req_msg
->data
, 4, (IPMI_REQ_HDR_LEN
-2)+dsize
);
456 rc
= smc_send_ipmi_message(fd
, req_msg
, rsp_msg
, poll_time
);
458 if (free_seqn
) { /* free seqn if library reserved it */
459 smc_seq
.d_addr
= dest
;
461 smc_seq
.seq_numbers
[0] = seq_no
;
462 scioc
.ic_cmd
= SCIOC_FREE_SEQN
;
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 "