xref: /titanic_41/usr/src/lib/libpcp/common/libpcp.c (revision d87bb7db56bb1b04f743195ba206a58728f6ffaf)
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 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Platform Channel Protocol Library functions on Nigara platforms
29  * (Ontario, Erie, etc..) Solaris applications use these interfaces
30  * to communicate with entities that reside on service processor.
31  */
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <assert.h>
40 #include <fcntl.h>
41 #include <errno.h>
42 #include <signal.h>
43 #include <setjmp.h>
44 #include <inttypes.h>
45 #include <umem.h>
46 #include <strings.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <sys/glvc.h>
50 #include <netinet/in.h>
51 
52 #include "libpcp.h"
53 #include "pcp_common.h"
54 
55 /*
56  * Following libpcp interfaces are exposed to user applications.
57  *
58  * int pcp_init(char *channel_name);
59  * int pcp_send_recv(int channel_fd, pcp_msg_t *req_msg, pcp_msg_t *resp_msg,
60  * 			uint32_t timeout);
61  * int pcp_close(int channel_fd);
62  *
63  */
64 
65 /*
66  * Forward declarations.
67  */
68 static int pcp_send_req_msg_hdr(pcp_req_msg_hdr_t *req_hdr);
69 static int pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr);
70 static int pcp_io_op(void *buf, int byte_cnt, int io_op);
71 static int pcp_get_xid(void);
72 static int pcp_get_prop(int channel_fd, int prop, unsigned int *val);
73 static int pcp_read(uint8_t *buf, int buf_len);
74 static int pcp_write(uint8_t *buf, int buf_len);
75 static int pcp_peek(uint8_t *buf, int buf_len);
76 static int pcp_peek_read(uint8_t *buf, int buf_len);
77 static int pcp_frame_error_handle(void);
78 static int check_magic_byte_presence(int byte_cnt, uint8_t *byte_val,
79 					int *ispresent);
80 static uint16_t checksum(uint16_t *addr, int32_t count);
81 static int pcp_cleanup(int channel_fd);
82 
83 /*
84  * local channel (glvc) file descriptor set by pcp_send_recv()
85  */
86 static int chnl_fd = -1;
87 
88 /*
89  * Message Transaction ID
90  */
91 static uint32_t msg_xid = 0;
92 
93 /*
94  * Channel MTU size.
95  */
96 static unsigned int mtu_size = PCPL_DEF_MTU_SZ;
97 
98 /*
99  * timeout field is supplied by user. timeout field is used to decide
100  * how long to block on glvc driver calls before we return timeout error
101  * to user applications.
102  *
103  * Note: In the current implementation of glvc driver, all glvc calls are
104  *       blocking.
105  */
106 static uint32_t glvc_timeout = 0;
107 
108 /*
109  * variables used by setsetjmp/siglongjmp.
110  */
111 static volatile sig_atomic_t jumpok = 0;
112 static sigjmp_buf jmpbuf;
113 
114 /*
115  * To unblock SIGALRM signal incase if it's blocked in libpcp user apps.
116  * Restore it to old state during pcp_close.
117  */
118 static sigset_t blkset;
119 
120 /*
121  * Buffers used for stream reading channel data. When data is read in
122  * stream fashion, first data is copied from channel (glvc) buffers to
123  * these local buffers from which the read requests are serviced.
124  */
125 #define	READ_AREA_SIZE	(2*mtu_size)
126 static uint8_t *read_head = NULL;
127 static uint8_t *read_tail = NULL;
128 static uint8_t *read_area = NULL;
129 
130 /*
131  * Buffer used for peeking new data available in channel (glvc) buffers.
132  */
133 #define	PEEK_AREA_SIZE	(mtu_size)
134 static uint8_t *peek_area = NULL;
135 
136 /*
137  * Buffers used for peeking data available either in local buffers or
138  * new data available in channel (glvc) buffers.
139  */
140 #define	PEEK_READ_AREA_SIZE	(2*mtu_size)
141 static uint8_t *peek_read_head = NULL;
142 static uint8_t *peek_read_tail = NULL;
143 static uint8_t *peek_read_area = NULL;
144 
145 static pcp_req_msg_hdr_t *req_msg_hdr = NULL;
146 static pcp_resp_msg_hdr_t *resp_msg_hdr = NULL;
147 static int req_msg_hdr_sz = 0;
148 static int resp_msg_hdr_sz = 0;
149 
150 /*
151  * signal handling variables to handle glvc blocking calls.
152  */
153 static struct sigaction glvc_act;
154 
155 /* To restore old SIGALRM signal handler */
156 static struct sigaction old_act;
157 
158 static void
159 glvc_timeout_handler(void)
160 {
161 	if (jumpok == 0)
162 		return;
163 	siglongjmp(jmpbuf, 1);
164 }
165 
166 /*
167  * Initialize the virtual channel. It basically opens the virtual channel
168  * provided by the host application.
169  *
170  */
171 
172 int
173 pcp_init(char *channel_name)
174 {
175 	sigset_t oldset;
176 	int channel_fd;
177 
178 	if (channel_name == NULL)
179 		return (PCPL_INVALID_ARGS);
180 	/*
181 	 * Open virtual channel name.
182 	 */
183 	if ((channel_fd = open(channel_name, O_RDWR|O_EXCL)) < 0) {
184 		return (PCPL_GLVC_ERROR);
185 	}
186 
187 	/*
188 	 * Initialize Message Transaction ID.
189 	 */
190 	msg_xid = PCPL_INIT_XID;
191 
192 	/*
193 	 * Get the Channel MTU size
194 	 */
195 
196 	if (pcp_get_prop(channel_fd, GLVC_XPORT_OPT_MTU_SZ, &mtu_size) != 0) {
197 		(void) close(channel_fd);
198 		return (PCPL_GLVC_ERROR);
199 	}
200 
201 	/*
202 	 * Get current signal mask. If SIGALRM is blocked
203 	 * unblock it.
204 	 */
205 	(void) sigprocmask(0, NULL, &oldset);
206 
207 	(void) sigemptyset(&blkset);
208 
209 	if (sigismember(&oldset, SIGALRM)) {
210 		(void) sigaddset(&blkset, SIGALRM);
211 		(void) sigprocmask(SIG_UNBLOCK, &blkset, NULL);
212 	}
213 	/*
214 	 * signal handler initialization to handle glvc call timeouts.
215 	 */
216 	glvc_act.sa_handler = glvc_timeout_handler;
217 	(void) sigemptyset(&glvc_act.sa_mask);
218 	glvc_act.sa_flags = SA_NODEFER;
219 
220 	if (sigaction(SIGALRM, &glvc_act, &old_act) < 0) {
221 		(void) close(channel_fd);
222 		return (PCPL_ERROR);
223 	}
224 
225 	return (channel_fd);
226 }
227 
228 /*
229  * Function: Close platform channel.
230  * Arguments:
231  *	int channel_fd - channel file descriptor.
232  * Returns:
233  *	always returns PCPL_OK for now.
234  */
235 int
236 pcp_close(int channel_fd)
237 {
238 
239 	if (channel_fd >= 0) {
240 		(void) pcp_cleanup(channel_fd);
241 		(void) close(channel_fd);
242 	} else {
243 		return (-1);
244 	}
245 
246 	/*
247 	 * free global buffers
248 	 */
249 	if (read_area != NULL) {
250 		umem_free(read_area, READ_AREA_SIZE);
251 		read_area = NULL;
252 	}
253 	if (peek_area != NULL) {
254 		umem_free(peek_area, PEEK_AREA_SIZE);
255 		peek_area = NULL;
256 	}
257 	if (peek_read_area != NULL) {
258 		umem_free(peek_read_area, PEEK_READ_AREA_SIZE);
259 		peek_read_area = NULL;
260 	}
261 	if (req_msg_hdr != NULL) {
262 		umem_free(req_msg_hdr, req_msg_hdr_sz);
263 		req_msg_hdr = NULL;
264 	}
265 	if (resp_msg_hdr != NULL) {
266 		umem_free(resp_msg_hdr, resp_msg_hdr_sz);
267 		resp_msg_hdr = NULL;
268 	}
269 
270 	/*
271 	 * Restore SIGALRM signal mask incase if we unblocked
272 	 * it during pcp_init.
273 	 */
274 	if (sigismember(&blkset, SIGALRM)) {
275 		(void) sigprocmask(SIG_BLOCK, &blkset, NULL);
276 	}
277 
278 	/* Restore SIGALRM signal handler */
279 	(void) sigaction(SIGALRM, &old_act, NULL);
280 
281 	return (PCPL_OK);
282 }
283 
284 /*
285  * Function: Send and Receive messages on platform channel.
286  * Arguments:
287  *	int channel_fd      - channel file descriptor.
288  *	pcp_msg_t *req_msg  - Request Message to send to other end of channel.
289  *	pcp_msg_t *resp_msg - Response Message to be received.
290  *	uint32_t timeout    - timeout field when waiting for data from channel.
291  * Returns:
292  *	0  - success (PCPL_OK).
293  *	(-ve) - failure:
294  *			PCPL_INVALID_ARGS - invalid args.
295  *			PCPL_GLVC_TIMEOUT - glvc call timeout.
296  *			PCPL_XPORT_ERROR - transport error in request message
297  *						noticed by receiver.
298  *			PCPL_MALLOC_FAIL - malloc failure.
299  *			PCPL_CKSUM_ERROR - checksum error.
300  */
301 int
302 pcp_send_recv(int channel_fd, pcp_msg_t *req_msg, pcp_msg_t *resp_msg,
303 		uint32_t timeout)
304 {
305 	void *datap;
306 	void *resp_msg_data = NULL;
307 	uint32_t status;
308 	uint16_t cksum = 0;
309 	int ret;
310 	int resp_hdr_ok;
311 #ifdef PCP_CKSUM_ENABLE
312 	uint16_t bkup_resp_hdr_cksum;
313 #endif
314 	if (channel_fd < 0) {
315 		return (PCPL_ERROR);
316 	}
317 
318 	/* copy channel_fd to local fd (chnl_fd) for other functions use */
319 	chnl_fd = channel_fd;
320 
321 	if (req_msg == NULL) {
322 		return (PCPL_INVALID_ARGS);
323 	}
324 
325 	if (timeout > 0)
326 		glvc_timeout = timeout;
327 	else
328 		glvc_timeout = 0;
329 
330 	if ((datap = req_msg->msg_data) == NULL)
331 		return (PCPL_INVALID_ARGS);
332 
333 	if (req_msg_hdr == NULL) {
334 		req_msg_hdr_sz = sizeof (pcp_req_msg_hdr_t);
335 		req_msg_hdr = (pcp_req_msg_hdr_t *)umem_zalloc(req_msg_hdr_sz,
336 								UMEM_DEFAULT);
337 		if (req_msg_hdr == NULL)
338 			return (PCPL_MALLOC_FAIL);
339 	}
340 
341 	if (req_msg->msg_len != 0) {
342 		/* calculate request msg_cksum */
343 		cksum = checksum((uint16_t *)datap, req_msg->msg_len);
344 	}
345 
346 	/*
347 	 * Fill in the message header for the request packet
348 	 */
349 	req_msg_hdr->magic_num = PCP_MAGIC_NUM;
350 	req_msg_hdr->proto_ver = PCP_PROT_VER_1;
351 	req_msg_hdr->msg_type = req_msg->msg_type;
352 	req_msg_hdr->sub_type = req_msg->sub_type;
353 	req_msg_hdr->rsvd_pad = 0;
354 	req_msg_hdr->xid = pcp_get_xid();
355 	req_msg_hdr->msg_len  = req_msg->msg_len;
356 	req_msg_hdr->timeout = timeout;
357 	req_msg_hdr->msg_cksum = cksum;
358 	req_msg_hdr->hdr_cksum = 0;
359 
360 	/* fill request header checksum */
361 	req_msg_hdr->hdr_cksum = checksum((uint16_t *)req_msg_hdr,
362 					req_msg_hdr_sz);
363 	/*
364 	 * set sig jmp location
365 	 */
366 	if (sigsetjmp(jmpbuf, 1)) {
367 		return (PCPL_GLVC_TIMEOUT);
368 	}
369 	jumpok = 1; /* monitor sigalrm from now on */
370 
371 	/*
372 	 * send request message header
373 	 */
374 	if ((ret = pcp_send_req_msg_hdr(req_msg_hdr))) {
375 
376 		return (ret);
377 	}
378 
379 	/*
380 	 * send request message
381 	 */
382 	if (req_msg->msg_len != 0) {
383 		if ((ret = pcp_io_op(datap, req_msg->msg_len,
384 					PCPL_IO_OP_WRITE))) {
385 			return (ret);
386 		}
387 	}
388 
389 	if (timeout == (uint32_t)PCP_TO_NO_RESPONSE)
390 		return (PCPL_OK);
391 
392 	if (resp_msg_hdr == NULL) {
393 		resp_msg_hdr_sz = sizeof (pcp_resp_msg_hdr_t);
394 		resp_msg_hdr = (pcp_resp_msg_hdr_t *)umem_alloc(resp_msg_hdr_sz,
395 								UMEM_DEFAULT);
396 		if (resp_msg_hdr == NULL)
397 			return (PCPL_MALLOC_FAIL);
398 	}
399 
400 	resp_hdr_ok = 0;
401 	while (!resp_hdr_ok) {
402 
403 		/*
404 		 * Receive response message header
405 		 * Note: frame error handling is done in
406 		 * 'pcp_recv_resp_msg_hdr()'.
407 		 */
408 		if ((ret = pcp_recv_resp_msg_hdr(resp_msg_hdr))) {
409 			return (ret);
410 		}
411 
412 		/*
413 		 * Check header checksum if it matches with the received hdr
414 		 * checksum.
415 		 */
416 #ifdef PCP_CKSUM_ENABLE
417 		bkup_resp_hdr_cksum = resp_msg_hdr->hdr_cksum;
418 		resp_msg_hdr->hdr_cksum = 0;
419 		cksum = checksum((uint16_t *)resp_msg_hdr, resp_msg_hdr_sz);
420 
421 		if (cksum != bkup_resp_hdr_cksum) {
422 			return (PCPL_CKSUM_ERROR);
423 		}
424 #endif
425 		/*
426 		 * Check for matching request and response messages
427 		 */
428 		if (resp_msg_hdr->xid != req_msg_hdr->xid) {
429 
430 			continue; /* continue reading response header */
431 		}
432 		resp_hdr_ok = 1;
433 	}
434 
435 	/*
436 	 * check status field for any channel protocol errrors
437 	 * This field signifies something happend during request
438 	 * message trasmission. This field is set by the receiver.
439 	 */
440 	status = resp_msg_hdr->status;
441 	if (status != PCP_OK) {
442 		return (PCPL_XPORT_ERROR);
443 	}
444 
445 	if (resp_msg_hdr->msg_len != 0) {
446 
447 		/* libpcp users should free this memory */
448 		if ((resp_msg_data = (uint8_t *)malloc(resp_msg_hdr->msg_len))
449 			== NULL)
450 			return (PCPL_MALLOC_FAIL);
451 		bzero(resp_msg_data, resp_msg_hdr->msg_len);
452 		/*
453 		 * Receive response message.
454 		 */
455 		if ((ret = pcp_io_op(resp_msg_data, resp_msg_hdr->msg_len,
456 						PCPL_IO_OP_READ))) {
457 			free(resp_msg_data);
458 			return (ret);
459 		}
460 
461 #ifdef PCP_CKSUM_ENABLE
462 		/* verify response message data checksum */
463 		cksum = checksum((uint16_t *)resp_msg_data,
464 					resp_msg_hdr->msg_len);
465 		if (cksum != resp_msg_hdr->msg_cksum) {
466 			free(resp_msg_data);
467 			return (PCPL_CKSUM_ERROR);
468 		}
469 #endif
470 	}
471 	/* Everything is okay put the received data into user */
472 	/* application's resp_msg struct */
473 	resp_msg->msg_len = resp_msg_hdr->msg_len;
474 	resp_msg->msg_type = resp_msg_hdr->msg_type;
475 	resp_msg->sub_type = resp_msg_hdr->sub_type;
476 	resp_msg->msg_data = (uint8_t *)resp_msg_data;
477 
478 	return (PCPL_OK);
479 
480 }
481 
482 /*
483  * Function: Get channel property values.
484  * Arguments:
485  *	int channel_fd - channel file descriptor.
486  *	int prop - property id.
487  *	unsigned int *val - property value tobe copied.
488  * Returns:
489  *	0 - success
490  *	(-ve) - failure:
491  *		PCPL_ERR_GLVC - glvc ioctl failure.
492  */
493 
494 static int
495 pcp_get_prop(int channel_fd, int prop, unsigned int *val)
496 {
497 	glvc_xport_opt_op_t	channel_op;
498 	int			ret;
499 
500 	channel_op.op_sel = GLVC_XPORT_OPT_GET;
501 	channel_op.opt_sel = prop;
502 	channel_op.opt_val = 0;
503 
504 	(void) alarm(glvc_timeout);
505 
506 	if ((ret = ioctl(channel_fd, GLVC_XPORT_IOCTL_OPT_OP,
507 		&channel_op)) < 0) {
508 
509 		(void) alarm(0);
510 		return (ret);
511 	}
512 	(void) alarm(0);
513 
514 	*val = channel_op.opt_val;
515 
516 	return (0);
517 }
518 
519 /*
520  * Function: wrapper for handling glvc calls (read/write/peek).
521  */
522 static int
523 pcp_io_op(void *buf, int byte_cnt, int io_op)
524 {
525 	int	rv;
526 	int	n;
527 	uint8_t	*datap;
528 	int	(*func_ptr)(uint8_t *, int);
529 	int	io_sz;
530 	int	try_cnt;
531 
532 
533 	if ((buf == NULL) || (byte_cnt < 0)) {
534 		return (PCPL_INVALID_ARGS);
535 	}
536 
537 	switch (io_op) {
538 		case PCPL_IO_OP_READ:
539 			func_ptr = pcp_read;
540 			break;
541 		case PCPL_IO_OP_WRITE:
542 			func_ptr = pcp_write;
543 			break;
544 		case PCPL_IO_OP_PEEK:
545 			func_ptr = pcp_peek;
546 			break;
547 		default:
548 			return (PCPL_INVALID_ARGS);
549 	}
550 
551 	/*
552 	 * loop until all I/O done, try limit exceded, or real failure
553 	 */
554 
555 	rv = 0;
556 	datap = buf;
557 	while (rv < byte_cnt) {
558 		io_sz = MIN((byte_cnt - rv), mtu_size);
559 		try_cnt = 0;
560 		while ((n = (*func_ptr)(datap, io_sz)) < 0) {
561 			try_cnt++;
562 			if (try_cnt > PCPL_MAX_TRY_CNT) {
563 				rv = n;
564 				goto done;
565 			}
566 			(void) sleep(PCPL_GLVC_SLEEP);
567 		} /* while trying the io operation */
568 
569 		if (n < 0) {
570 			rv = n;
571 			goto done;
572 		}
573 		rv += n;
574 		datap += n;
575 	} /* while still have more data */
576 
577 done:
578 	if (rv == byte_cnt)
579 		return (0);
580 	else
581 		return (PCPL_GLVC_ERROR);
582 }
583 
584 /*
585  * For peeking 'bytes_cnt' bytes in channel (glvc) buffers.
586  * If data is available, the data is copied into 'buf'.
587  */
588 static int
589 pcp_peek(uint8_t *buf, int bytes_cnt)
590 {
591 	int			ret;
592 	glvc_xport_msg_peek_t	peek_ctrl;
593 	int			n, m;
594 
595 	if (bytes_cnt < 0 || bytes_cnt > mtu_size) {
596 		return (PCPL_INVALID_ARGS);
597 	}
598 
599 	/*
600 	 * initialization of buffers used for peeking data in channel buffers.
601 	 */
602 	if (peek_area == NULL) {
603 		peek_area = (uint8_t *)umem_zalloc(PEEK_AREA_SIZE,
604 							UMEM_DEFAULT);
605 		if (peek_area == NULL) {
606 			return (PCPL_MALLOC_FAIL);
607 		}
608 	}
609 
610 	/*
611 	 * peek max MTU size bytes
612 	 */
613 	peek_ctrl.buf = (caddr_t)peek_area;
614 	peek_ctrl.buflen = mtu_size;
615 	peek_ctrl.flags = 0;
616 
617 	(void) alarm(glvc_timeout);
618 
619 	if ((ret = ioctl(chnl_fd, GLVC_XPORT_IOCTL_DATA_PEEK, &peek_ctrl))
620 		< 0) {
621 		(void) alarm(0);
622 		return (ret);
623 	}
624 	(void) alarm(0);
625 
626 	n = peek_ctrl.buflen;
627 
628 	if (n < 0)
629 		return (PCPL_GLVC_ERROR);
630 
631 	/*
632 	 * satisfy request as best as we can
633 	 */
634 	m = MIN(bytes_cnt, n);
635 	(void) memcpy(buf, peek_area, m);
636 
637 	return (m);
638 
639 }
640 
641 /*
642  * Function: write 'byte_cnt' bytes from 'buf' to channel.
643  */
644 static int
645 pcp_write(uint8_t *buf, int byte_cnt)
646 {
647 
648 	int	ret;
649 
650 	/* check for valid arguments */
651 	if (buf == NULL || byte_cnt < 0 || byte_cnt > mtu_size) {
652 		return (PCPL_INVALID_ARGS);
653 	}
654 
655 	(void) alarm(glvc_timeout);
656 
657 	if ((ret = write(chnl_fd, buf, byte_cnt)) < 0) {
658 		(void) alarm(0);
659 		return (ret);
660 	}
661 	(void) alarm(0);
662 
663 	return (ret);
664 }
665 
666 /*
667  * In current implementaion of glvc driver, streams reads are not supported.
668  * pcp_read mimics stream reads by first reading all the bytes present in the
669  * channel buffer into a local buffer and from then on read requests
670  * are serviced from local buffer. When read requests are not serviceble
671  * from local buffer, it repeates by first reading data from channel buffers.
672  *
673  * This call may need to be enhanced when glvc supports buffered (stream)
674  * reads - TBD
675  */
676 
677 static int
678 pcp_read(uint8_t *buf, int byte_cnt)
679 {
680 	int			ret;
681 	int			n, m, i;
682 
683 	if (byte_cnt < 0 || byte_cnt > mtu_size) {
684 		return (PCPL_INVALID_ARGS);
685 	}
686 
687 	/*
688 	 * initialization of local read buffer
689 	 * from which the stream read requests are serviced.
690 	 */
691 	if (read_area == NULL) {
692 		read_area = (uint8_t *)umem_zalloc(READ_AREA_SIZE,
693 							UMEM_DEFAULT);
694 		if (read_area == NULL) {
695 			return (PCPL_MALLOC_FAIL);
696 		}
697 		read_head = read_area;
698 		read_tail = read_area;
699 	}
700 
701 	/*
702 	 * if we already read this data then copy from local buffer it self
703 	 * without calling new read.
704 	 */
705 	if (byte_cnt <= (read_tail - read_head)) {
706 		(void) memcpy(buf, read_head, byte_cnt);
707 		read_head += byte_cnt;
708 		return (byte_cnt);
709 	}
710 
711 	/*
712 	 * if the request is not satisfied from the buffered data, then move the
713 	 * remaining data to front of the buffer and read new data.
714 	 */
715 	for (i = 0; i < (read_tail - read_head); ++i) {
716 		read_area[i] = read_head[i];
717 	}
718 	read_head = read_area;
719 	read_tail = read_head + i;
720 
721 	/*
722 	 * do a peek to see how much data is available and read complete data.
723 	 */
724 
725 	if ((m = pcp_peek(read_tail, mtu_size)) < 0) {
726 		return (m);
727 	}
728 
729 	(void) alarm(glvc_timeout);
730 	if ((ret = read(chnl_fd, read_tail, m)) < 0) {
731 		(void) alarm(0);
732 		return (ret);
733 	}
734 
735 	(void) alarm(0);
736 	read_tail += ret;
737 
738 	/*
739 	 * copy the requested bytes.
740 	 */
741 	n = MIN(byte_cnt, (read_tail - read_head));
742 	(void) memcpy(buf, read_head, n);
743 
744 	read_head += n;
745 
746 	return (n);
747 }
748 
749 /*
750  * This function is slight different from pcp_peek. The peek requests are first
751  * serviced from local read buffer, if data is available. If the peek request
752  * is not serviceble from local read buffer, then the data is peeked from
753  * channel buffer. This function is mainly used for proper protocol framing
754  * error handling.
755  */
756 static int
757 pcp_peek_read(uint8_t *buf, int byte_cnt)
758 {
759 	int	n, m, i;
760 
761 	if (byte_cnt < 0 || byte_cnt > mtu_size) {
762 		return (PCPL_INVALID_ARGS);
763 	}
764 
765 	/*
766 	 * initialization of peek_read buffer.
767 	 */
768 	if (peek_read_area == NULL) {
769 		peek_read_area = (uint8_t *)umem_zalloc(PEEK_READ_AREA_SIZE,
770 						UMEM_DEFAULT);
771 		if (peek_read_area == NULL) {
772 			return (PCPL_MALLOC_FAIL);
773 		}
774 		peek_read_head = peek_read_area;
775 		peek_read_tail = peek_read_area;
776 	}
777 
778 	/*
779 	 * if we already have the data in local read buffer then copy
780 	 * from local buffer it self w/out calling new peek
781 	 */
782 	if (byte_cnt <= (read_tail - read_head)) {
783 		(void) memcpy(buf, read_head, byte_cnt);
784 		return (byte_cnt);
785 	}
786 
787 	/*
788 	 * if the request is not satisfied from local read buffer, then first
789 	 * copy the remaining data in local read buffer to peek_read_area and
790 	 * then issue new peek.
791 	 */
792 	for (i = 0; i < (read_tail - read_head); ++i) {
793 		peek_read_area[i] = read_head[i];
794 	}
795 	peek_read_head = peek_read_area;
796 	peek_read_tail = peek_read_head + i;
797 
798 	/*
799 	 * do a peek to see how much data is available and read complete data.
800 	 */
801 
802 	if ((m = pcp_peek(peek_read_tail, mtu_size)) < 0) {
803 		return (m);
804 	}
805 
806 	peek_read_tail += m;
807 
808 	/*
809 	 * copy the requested bytes
810 	 */
811 	n = MIN(byte_cnt, (peek_read_tail - peek_read_head));
812 	(void) memcpy(buf, peek_read_head, n);
813 
814 	return (n);
815 }
816 
817 /*
818  * Send Request Message Header.
819  */
820 static int
821 pcp_send_req_msg_hdr(pcp_req_msg_hdr_t *req_hdr)
822 {
823 	pcp_req_msg_hdr_t	*hdrp;
824 	int			hdr_sz;
825 	int			ret;
826 
827 	hdr_sz = sizeof (pcp_req_msg_hdr_t);
828 	if ((hdrp = (pcp_req_msg_hdr_t *)umem_zalloc(hdr_sz,
829 						UMEM_DEFAULT)) == NULL) {
830 		return (PCPL_MALLOC_FAIL);
831 	}
832 
833 	hdrp->magic_num = htonl(req_hdr->magic_num);
834 	hdrp->proto_ver = req_hdr->proto_ver;
835 	hdrp->msg_type = req_hdr->msg_type;
836 	hdrp->sub_type = req_hdr->sub_type;
837 	hdrp->rsvd_pad = htons(req_hdr->rsvd_pad);
838 	hdrp->xid = htonl(req_hdr->xid);
839 	hdrp->timeout = htonl(req_hdr->timeout);
840 	hdrp->msg_len = htonl(req_hdr->msg_len);
841 	hdrp->msg_cksum = htons(req_hdr->msg_cksum);
842 	hdrp->hdr_cksum = htons(req_hdr->hdr_cksum);
843 
844 	if ((ret = pcp_io_op((char *)hdrp, hdr_sz, PCPL_IO_OP_WRITE)) != 0) {
845 		umem_free(hdrp, hdr_sz);
846 		return (ret);
847 	}
848 	umem_free(hdrp, hdr_sz);
849 	return (PCP_OK);
850 }
851 
852 /*
853  * Receive Response message header.
854  */
855 static int
856 pcp_recv_resp_msg_hdr(pcp_resp_msg_hdr_t *resp_hdr)
857 {
858 	uint32_t	magic_num;
859 	uint8_t		proto_ver;
860 	uint8_t		msg_type;
861 	uint8_t		sub_type;
862 	uint8_t		rsvd_pad;
863 	uint32_t	xid;
864 	uint32_t	timeout;
865 	uint32_t	msg_len;
866 	uint32_t	status;
867 	uint16_t	msg_cksum;
868 	uint16_t	hdr_cksum;
869 	int		ret;
870 
871 	if (resp_hdr == NULL) {
872 		return (PCPL_INVALID_ARGS);
873 	}
874 
875 	/*
876 	 * handle protocol framing errors.
877 	 * pcp_frame_error_handle() returns when proper frame arrived
878 	 * (magic seq) or if an error happens while reading data from
879 	 * channel.
880 	 */
881 	if ((ret = pcp_frame_error_handle()) != 0)
882 		return (PCPL_FRAME_ERROR);
883 
884 	/* read magic number first */
885 	if ((ret = pcp_io_op(&magic_num, sizeof (magic_num),
886 					PCPL_IO_OP_READ)) != 0) {
887 		return (ret);
888 	}
889 
890 	magic_num = ntohl(magic_num);
891 
892 	if (magic_num != PCP_MAGIC_NUM) {
893 		return (PCPL_FRAME_ERROR);
894 	}
895 
896 	/* read version field */
897 	if ((ret = pcp_io_op(&proto_ver, sizeof (proto_ver),
898 					PCPL_IO_OP_READ)) != 0) {
899 		return (ret);
900 	}
901 
902 	/* check protocol version */
903 	if (proto_ver != PCP_PROT_VER_1) {
904 		return (PCPL_PROT_ERROR);
905 	}
906 
907 	/* Read message type */
908 	if ((ret = pcp_io_op(&msg_type, sizeof (msg_type),
909 					PCPL_IO_OP_READ)) != 0) {
910 		return (ret);
911 	}
912 
913 	/* Read message sub type */
914 	if ((ret = pcp_io_op(&sub_type, sizeof (sub_type),
915 					PCPL_IO_OP_READ)) != 0) {
916 		return (ret);
917 	}
918 
919 	/* Read rcvd_pad bits */
920 	if ((ret = pcp_io_op(&rsvd_pad, sizeof (rsvd_pad),
921 					PCPL_IO_OP_READ)) != 0) {
922 		return (ret);
923 	}
924 
925 	/* receive transaction id */
926 	if ((ret = pcp_io_op(&xid, sizeof (xid),
927 			PCPL_IO_OP_READ)) != 0) {
928 		return (ret);
929 	}
930 
931 	xid = ntohl(xid);
932 
933 	/* receive timeout value */
934 	if ((ret = pcp_io_op(&timeout, sizeof (timeout),
935 				PCPL_IO_OP_READ)) != 0) {
936 		return (ret);
937 	}
938 
939 	timeout = ntohl(timeout);
940 
941 	/* receive message length */
942 	if ((ret = pcp_io_op(&msg_len, sizeof (msg_len),
943 				PCPL_IO_OP_READ)) != 0) {
944 		return (ret);
945 	}
946 
947 	msg_len = ntohl(msg_len);
948 
949 	/* receive status field */
950 	if ((ret = pcp_io_op(&status, sizeof (status),
951 				PCPL_IO_OP_READ)) != 0) {
952 		return (ret);
953 	}
954 
955 	status = ntohl(status);
956 
957 	/* receive message checksum */
958 	if ((ret = pcp_io_op(&msg_cksum, sizeof (msg_cksum),
959 					PCPL_IO_OP_READ)) != 0) {
960 		return (ret);
961 	}
962 
963 	msg_cksum = ntohs(msg_cksum);
964 
965 	/* receive header checksum */
966 	if ((ret = pcp_io_op(&hdr_cksum, sizeof (hdr_cksum),
967 					PCPL_IO_OP_READ)) != 0) {
968 		return (ret);
969 	}
970 
971 	hdr_cksum = ntohs(hdr_cksum);
972 
973 	/* copy to resp_hdr */
974 
975 	resp_hdr->magic_num = magic_num;
976 	resp_hdr->proto_ver = proto_ver;
977 	resp_hdr->msg_type = msg_type;
978 	resp_hdr->sub_type = sub_type;
979 	resp_hdr->rsvd_pad = rsvd_pad;
980 	resp_hdr->xid = xid;
981 	resp_hdr->timeout = timeout;
982 	resp_hdr->msg_len = msg_len;
983 	resp_hdr->status = status;
984 	resp_hdr->msg_cksum = msg_cksum;
985 	resp_hdr->hdr_cksum = hdr_cksum;
986 
987 	return (PCP_OK);
988 }
989 
990 /*
991  * Get next xid for including in request message.
992  * Every request and response message are matched
993  * for same xid.
994  */
995 static int pcp_get_xid(void)
996 {
997 	uint32_t ret;
998 
999 	ret = msg_xid;
1000 	if (msg_xid == PCPL_MAX_XID)
1001 		msg_xid = PCPL_MIN_XID;
1002 	else
1003 		++msg_xid;
1004 	return (ret);
1005 }
1006 
1007 /*
1008  * This function handles channel framing errors. It waits until proper
1009  * frame with starting sequence as magic numder (0xAFBCAFA0)
1010  * is arrived. It removes unexpected data (before the magic number sequence)
1011  * on the channel. It returns when proper magic number sequence is seen
1012  * or when any failure happens while reading/peeking the channel.
1013  */
1014 static int
1015 pcp_frame_error_handle(void)
1016 {
1017 	uint8_t		magic_num_buf[4];
1018 	int		ispresent = 0;
1019 	uint32_t	net_magic_num; /* magic byte in network byte order */
1020 	uint32_t	host_magic_num = PCP_MAGIC_NUM;
1021 	uint8_t		buf[2];
1022 
1023 	net_magic_num =  htonl(host_magic_num);
1024 	(void) memcpy(magic_num_buf, (uint8_t *)&net_magic_num, 4);
1025 
1026 	while (!ispresent) {
1027 		/*
1028 		 * Check if next four bytes matches pcp magic number.
1029 		 * if mathing not found, discard 1 byte and continue checking.
1030 		 */
1031 		if (!check_magic_byte_presence(4, &magic_num_buf[0],
1032 							&ispresent)) {
1033 			if (!ispresent) {
1034 				/* remove 1 byte */
1035 				(void) pcp_io_op(buf, 1, PCPL_IO_OP_READ);
1036 			}
1037 		} else {
1038 			return (-1);
1039 		}
1040 	}
1041 
1042 	return (0);
1043 }
1044 
1045 /*
1046  * checks whether certain byte sequence is present in the data stream.
1047  */
1048 static int
1049 check_magic_byte_presence(int byte_cnt, uint8_t *byte_seq, int *ispresent)
1050 {
1051 	int		ret, i;
1052 	uint8_t		buf[4];
1053 
1054 	if ((ret = pcp_peek_read(buf, byte_cnt)) < 0) {
1055 		return (ret);
1056 	}
1057 
1058 	/* 'byte_cnt' bytes not present */
1059 	if (ret != byte_cnt) {
1060 		*ispresent = 0;
1061 		return (0);
1062 	}
1063 
1064 	for (i = 0; i < byte_cnt; ++i) {
1065 		if (buf[i] != byte_seq[i]) {
1066 			*ispresent = 0;
1067 			return (0);
1068 		}
1069 	}
1070 	*ispresent = 1;
1071 
1072 	return (0);
1073 }
1074 
1075 /*
1076  * 16-bit simple internet checksum
1077  */
1078 static uint16_t
1079 checksum(uint16_t *addr, int32_t count)
1080 {
1081 	/*
1082 	 * Compute Internet Checksum for "count" bytes
1083 	 * beginning at location "addr".
1084 	 */
1085 
1086 	register uint32_t	sum = 0;
1087 
1088 	while (count > 1)  {
1089 		/*  This is the inner loop */
1090 		sum += *(unsigned short *)addr++;
1091 		count -= 2;
1092 	}
1093 
1094 	/*  Add left-over byte, if any */
1095 	if (count > 0)
1096 		sum += * (unsigned char *)addr;
1097 
1098 	/* Fold 32-bit sum to 16 bits */
1099 	while (sum >> 16)
1100 		sum = (sum & 0xffff) + (sum >> 16);
1101 
1102 	sum = (~sum) & 0xffff;
1103 	if (sum == 0)
1104 		sum = 0xffff;
1105 
1106 	return (sum);
1107 }
1108 
1109 /*
1110  * cleanup the channel if any data is hanging in
1111  * channel buffers.
1112  */
1113 static int
1114 pcp_cleanup(int channel_fd)
1115 {
1116 	int			ret;
1117 	glvc_xport_msg_peek_t	peek_ctrl;
1118 	int			n, done;
1119 	uint8_t			*buf = NULL;
1120 	int			retry = 0;
1121 
1122 
1123 	buf = (uint8_t *)umem_zalloc((mtu_size), UMEM_DEFAULT);
1124 	if (buf == NULL) {
1125 		return (PCPL_MALLOC_FAIL);
1126 	}
1127 
1128 	peek_ctrl.buf = (caddr_t)buf;
1129 	peek_ctrl.buflen = mtu_size;
1130 	peek_ctrl.flags = 0;
1131 
1132 	/*
1133 	 * set sig jmp location
1134 	 */
1135 	if (sigsetjmp(jmpbuf, 1)) {
1136 		umem_free(buf, mtu_size);
1137 		return (PCPL_GLVC_TIMEOUT);
1138 	}
1139 
1140 	done = 0;
1141 	while (!done) {
1142 
1143 		(void) alarm(PCP_CLEANUP_TIMEOUT);
1144 		if ((ret = ioctl(channel_fd, GLVC_XPORT_IOCTL_DATA_PEEK,
1145 							&peek_ctrl)) < 0) {
1146 			(void) alarm(0);
1147 			done = 1;
1148 			continue;
1149 		}
1150 		(void) alarm(0);
1151 
1152 		n = peek_ctrl.buflen;
1153 
1154 		if (n <= 0 && retry > 2) {
1155 			done = 1;
1156 			continue;
1157 		} else if (n <= 0) {
1158 			++retry;
1159 			continue;
1160 		}
1161 
1162 		/* remove data from channel */
1163 		(void) alarm(PCP_CLEANUP_TIMEOUT);
1164 		if ((ret = read(channel_fd, buf, n)) < 0) {
1165 			(void) alarm(0);
1166 			done = 1;
1167 			continue;
1168 		}
1169 		(void) alarm(0);
1170 	}
1171 
1172 	umem_free(buf, mtu_size);
1173 	return (ret);
1174 }
1175