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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2006 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 #include <assert.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <alloca.h>
36 #include <signal.h>
37 #include <strings.h>
38 #include <sys/param.h>
39 #include <sys/brand.h>
40 #include <sys/poll.h>
41 #include <sys/syscall.h>
42 #include <sys/lx_debug.h>
43 #include <sys/lx_poll.h>
44 #include <sys/lx_syscall.h>
45 #include <sys/lx_brand.h>
46 #include <sys/lx_misc.h>
47
48 extern int select_large_fdset(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0,
49 struct timeval *tv);
50
51 int
52 lx_select(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
53 uintptr_t p5)
54 {
55 int nfds = (int)p1;
56 fd_set *rfdsp = NULL;
57 fd_set *wfdsp = NULL;
58 fd_set *efdsp = NULL;
59 struct timeval tv, *tvp = NULL;
60 int fd_set_len = howmany(nfds, 8);
61 int r;
62 hrtime_t start = NULL, end;
63
64 lx_debug("\tselect(%d, 0x%p, x%p, 0x%p. 0x%p, 0x%p)",
65 nfds, rfdsp, wfdsp, efdsp, tvp);
66
67 if (nfds > 0) {
68 if (p2 != NULL) {
69 rfdsp = SAFE_ALLOCA(fd_set_len);
70 if (rfdsp == NULL)
71 return (-ENOMEM);
72 if (uucopy((void *)p2, rfdsp, fd_set_len) != 0)
73 return (-errno);
74 }
75 if (p3 != NULL) {
76 wfdsp = SAFE_ALLOCA(fd_set_len);
77 if (wfdsp == NULL)
78 return (-ENOMEM);
79 if (uucopy((void *)p3, wfdsp, fd_set_len) != 0)
80 return (-errno);
81 }
82 if (p4 != NULL) {
83 efdsp = SAFE_ALLOCA(fd_set_len);
84 if (efdsp == NULL)
85 return (-ENOMEM);
86 if (uucopy((void *)p4, efdsp, fd_set_len) != 0)
87 return (-errno);
88 }
89 }
90 if (p5 != NULL) {
91 tvp = &tv;
92 if (uucopy((void *)p5, &tv, sizeof (tv)) != 0)
93 return (-errno);
94 start = gethrtime();
95 }
96
97 if (nfds >= FD_SETSIZE)
98 r = select_large_fdset(nfds, rfdsp, wfdsp, efdsp, tvp);
99 else
100 r = select(nfds, rfdsp, wfdsp, efdsp, tvp);
101 if (r < 0)
102 return (-errno);
103
104 if (tvp != NULL) {
105 long long tv_total;
106
107 /*
108 * Linux updates the timeval parameter for select() calls
109 * with the amount of time that left before the select
110 * would have timed out.
111 */
112 end = gethrtime();
113 tv_total = (tv.tv_sec * MICROSEC) + tv.tv_usec;
114 tv_total -= ((end - start) / (NANOSEC / MICROSEC));
115 if (tv_total < 0) {
116 tv.tv_sec = 0;
117 tv.tv_usec = 0;
118 } else {
119 tv.tv_sec = tv_total / MICROSEC;
120 tv.tv_usec = tv_total % MICROSEC;
121 }
122
123 if (uucopy(&tv, (void *)p5, sizeof (tv)) != 0)
124 return (-errno);
125 }
126
127 if ((rfdsp != NULL) && (uucopy(rfdsp, (void *)p2, fd_set_len) != 0))
128 return (-errno);
129 if ((wfdsp != NULL) && (uucopy(wfdsp, (void *)p3, fd_set_len) != 0))
130 return (-errno);
131 if ((efdsp != NULL) && (uucopy(efdsp, (void *)p4, fd_set_len) != 0))
132 return (-errno);
133
134 return (r);
135 }
136
137 int
138 lx_poll(uintptr_t p1, uintptr_t p2, uintptr_t p3)
139 {
140 struct pollfd *lfds, *sfds;
141 nfds_t nfds = (nfds_t)p2;
142 int fds_size, i, rval, revents;
143
144 /*
145 * Note: we are assuming that the Linux and Solaris pollfd
146 * structures are identical. Copy in the linux poll structure.
147 */
148 fds_size = sizeof (struct pollfd) * nfds;
149 lfds = (struct pollfd *)SAFE_ALLOCA(fds_size);
150 if (lfds == NULL)
151 return (-ENOMEM);
152 if (uucopy((void *)p1, lfds, fds_size) != 0)
153 return (-errno);
154
155 /*
156 * The poll system call modifies the poll structures passed in
157 * so we'll need to make an exra copy of them.
158 */
159 sfds = (struct pollfd *)SAFE_ALLOCA(fds_size);
160 if (sfds == NULL)
161 return (-ENOMEM);
162
163 /* Convert the Linux events bitmask into the Solaris equivalent. */
164 for (i = 0; i < nfds; i++) {
165 /*
166 * If the caller is polling for an unsupported event, we
167 * have to bail out.
168 */
169 if (lfds[i].events & ~LX_POLL_SUPPORTED_EVENTS) {
170 lx_unsupported("unsupported poll events requested: "
171 "events=0x%x", lfds[i].events);
172 return (-ENOTSUP);
173 }
174
175 sfds[i].fd = lfds[i].fd;
176 sfds[i].events = lfds[i].events & LX_POLL_COMMON_EVENTS;
177 if (lfds[i].events & LX_POLLWRNORM)
178 sfds[i].events |= POLLWRNORM;
179 if (lfds[i].events & LX_POLLWRBAND)
180 sfds[i].events |= POLLWRBAND;
181 sfds[i].revents = 0;
182 }
183
184 lx_debug("\tpoll(0x%p, %u, %d)", sfds, nfds, (int)p3);
185
186 if ((rval = poll(sfds, nfds, (int)p3)) < 0)
187 return (-errno);
188
189 /* Convert the Solaris revents bitmask into the Linux equivalent */
190 for (i = 0; i < nfds; i++) {
191 revents = sfds[i].revents & LX_POLL_COMMON_EVENTS;
192 if (sfds[i].revents & POLLWRBAND)
193 revents |= LX_POLLWRBAND;
194
195 /*
196 * Be carefull because on solaris POLLOUT and POLLWRNORM
197 * are defined to the same values but on linux they
198 * are not.
199 */
200 if (sfds[i].revents & POLLOUT) {
201 if ((lfds[i].events & LX_POLLOUT) == 0)
202 revents &= ~LX_POLLOUT;
203 if (lfds[i].events & LX_POLLWRNORM)
204 revents |= LX_POLLWRNORM;
205 }
206
207 lfds[i].revents = revents;
208 }
209
210 /* Copy out the results */
211 if (uucopy(lfds, (void *)p1, fds_size) != 0)
212 return (-errno);
213
214 return (rval);
215 }