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