xref: /titanic_52/usr/src/cmd/picl/plugins/sun4u/snowbird/lib/libctsmc/libctsmc.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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