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, 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 }