1 /*
2  * Copyright (c) 2018 Linaro Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/syscall_handler.h>
9 #include <zephyr/sys/math_extras.h>
10 #include <zephyr/net/socket.h>
11 #include "sockets_internal.h"
12 
13 /* Get size, in elements, of an array within a struct. */
14 #define STRUCT_MEMBER_ARRAY_SIZE(type, field) ARRAY_SIZE(((type *)0)->field)
15 
16 /* Returns results in word_idx and bit_mask "output" params */
17 #define FD_SET_CALC_OFFSETS(set, word_idx, bit_mask) { \
18 	unsigned int b_idx = fd % (sizeof(set->bitset[0]) * 8); \
19 	word_idx = fd / (sizeof(set->bitset[0]) * 8); \
20 	bit_mask = 1 << b_idx; \
21 	}
22 
ZSOCK_FD_ZERO(zsock_fd_set * set)23 void ZSOCK_FD_ZERO(zsock_fd_set *set)
24 {
25 	int i;
26 
27 	for (i = 0; i < ARRAY_SIZE(set->bitset); i++) {
28 		set->bitset[i] = 0U;
29 	}
30 }
31 
ZSOCK_FD_ISSET(int fd,zsock_fd_set * set)32 int ZSOCK_FD_ISSET(int fd, zsock_fd_set *set)
33 {
34 	uint32_t word_idx, bit_mask;
35 
36 	if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
37 		return 0;
38 	}
39 
40 	FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
41 
42 	return (set->bitset[word_idx] & bit_mask) != 0U;
43 }
44 
ZSOCK_FD_CLR(int fd,zsock_fd_set * set)45 void ZSOCK_FD_CLR(int fd, zsock_fd_set *set)
46 {
47 	uint32_t word_idx, bit_mask;
48 
49 	if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
50 		return;
51 	}
52 
53 	FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
54 
55 	set->bitset[word_idx] &= ~bit_mask;
56 }
57 
ZSOCK_FD_SET(int fd,zsock_fd_set * set)58 void ZSOCK_FD_SET(int fd, zsock_fd_set *set)
59 {
60 	uint32_t word_idx, bit_mask;
61 
62 	if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
63 		return;
64 	}
65 
66 	FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
67 
68 	set->bitset[word_idx] |= bit_mask;
69 }
70 
z_impl_zsock_select(int nfds,zsock_fd_set * readfds,zsock_fd_set * writefds,zsock_fd_set * exceptfds,struct zsock_timeval * timeout)71 int z_impl_zsock_select(int nfds, zsock_fd_set *readfds, zsock_fd_set *writefds,
72 			zsock_fd_set *exceptfds, struct zsock_timeval *timeout)
73 {
74 	struct zsock_pollfd pfds[CONFIG_NET_SOCKETS_POLL_MAX];
75 	k_timeout_t poll_timeout;
76 	int i, res;
77 	int num_pfds = 0;
78 	int num_selects = 0;
79 	int fd_no = 0;
80 
81 	for (i = 0; i < STRUCT_MEMBER_ARRAY_SIZE(zsock_fd_set, bitset); i++) {
82 		uint32_t bit_mask = 1U;
83 		uint32_t read_mask = 0U, write_mask = 0U, except_mask = 0U;
84 		uint32_t ored_mask;
85 
86 		if (readfds != NULL) {
87 			read_mask = readfds->bitset[i];
88 		}
89 
90 		if (writefds != NULL) {
91 			write_mask = writefds->bitset[i];
92 		}
93 
94 		if (exceptfds != NULL) {
95 			except_mask = exceptfds->bitset[i];
96 		}
97 
98 		ored_mask = read_mask | write_mask | except_mask;
99 		if (ored_mask == 0U) {
100 			fd_no += sizeof(ored_mask) * 8;
101 			continue;
102 		}
103 
104 		do {
105 			if (ored_mask & bit_mask) {
106 				int events = 0;
107 
108 				if (num_pfds >= ARRAY_SIZE(pfds)) {
109 					errno = ENOMEM;
110 					return -1;
111 				}
112 
113 				if (read_mask & bit_mask) {
114 					events |= ZSOCK_POLLIN;
115 				}
116 
117 				if (write_mask & bit_mask) {
118 					events |= ZSOCK_POLLOUT;
119 				}
120 
121 				if (except_mask & bit_mask) {
122 					events |= ZSOCK_POLLPRI;
123 				}
124 
125 				pfds[num_pfds].fd = fd_no;
126 				pfds[num_pfds++].events = events;
127 			}
128 
129 			bit_mask <<= 1;
130 			fd_no++;
131 		} while (bit_mask != 0U);
132 	}
133 
134 	if (timeout == NULL) {
135 		poll_timeout = K_FOREVER;
136 	} else {
137 		poll_timeout =
138 			K_USEC(timeout->tv_sec * 1000000UL + timeout->tv_usec);
139 	}
140 
141 	res = zsock_poll_internal(pfds, num_pfds, poll_timeout);
142 	if (res == -1) {
143 		return -1;
144 	}
145 
146 	if (readfds != NULL) {
147 		ZSOCK_FD_ZERO(readfds);
148 	}
149 
150 	if (writefds != NULL) {
151 		ZSOCK_FD_ZERO(writefds);
152 	}
153 
154 	if (exceptfds != NULL) {
155 		ZSOCK_FD_ZERO(exceptfds);
156 	}
157 
158 	for (i = 0; i < num_pfds && res > 0; i++) {
159 		short revents = pfds[i].revents;
160 		int fd = pfds[i].fd;
161 
162 		if (revents == 0) {
163 			continue;
164 		}
165 
166 		/* POSIX: "EBADF: One or more of the file descriptor sets
167 		 * specified a file descriptor that is not a valid open
168 		 * file descriptor."
169 		 * So, unlike poll(), a single invalid fd aborts the entire
170 		 * select().
171 		 */
172 		if (revents & ZSOCK_POLLNVAL) {
173 			errno = EBADF;
174 			return -1;
175 		}
176 
177 		if (revents & ZSOCK_POLLIN) {
178 			if (readfds != NULL) {
179 				ZSOCK_FD_SET(fd, readfds);
180 				num_selects++;
181 			}
182 		}
183 
184 		if (revents & ZSOCK_POLLOUT) {
185 			if (writefds != NULL) {
186 				ZSOCK_FD_SET(fd, writefds);
187 				num_selects++;
188 			}
189 		}
190 
191 		/* It's unclear if HUP/ERR belong here. At least not ignore
192 		 * them. Zephyr doesn't use HUP and barely use ERR so far.
193 		 */
194 		if (revents & (ZSOCK_POLLPRI | ZSOCK_POLLHUP | ZSOCK_POLLERR)) {
195 			if (exceptfds != NULL) {
196 				ZSOCK_FD_SET(fd, exceptfds);
197 				num_selects++;
198 			}
199 		}
200 
201 		res--;
202 	}
203 
204 	return num_selects;
205 }
206 
207 #ifdef CONFIG_USERSPACE
z_vrfy_zsock_select(int nfds,zsock_fd_set * readfds,zsock_fd_set * writefds,zsock_fd_set * exceptfds,struct zsock_timeval * timeout)208 static int z_vrfy_zsock_select(int nfds, zsock_fd_set *readfds,
209 			       zsock_fd_set *writefds,
210 			       zsock_fd_set *exceptfds,
211 			       struct zsock_timeval *timeout)
212 {
213 	zsock_fd_set *readfds_copy = NULL, *writefds_copy = NULL,
214 		*exceptfds_copy = NULL;
215 	struct zsock_timeval *timeval = NULL;
216 	int ret = -1;
217 
218 	if (readfds) {
219 		readfds_copy = z_user_alloc_from_copy((void *)readfds,
220 						      sizeof(zsock_fd_set));
221 		if (!readfds_copy) {
222 			errno = ENOMEM;
223 			goto out;
224 		}
225 	}
226 
227 	if (writefds) {
228 		writefds_copy = z_user_alloc_from_copy((void *)writefds,
229 						       sizeof(zsock_fd_set));
230 		if (!writefds_copy) {
231 			errno = ENOMEM;
232 			goto out;
233 		}
234 	}
235 
236 	if (exceptfds) {
237 		exceptfds_copy = z_user_alloc_from_copy((void *)exceptfds,
238 							sizeof(zsock_fd_set));
239 		if (!exceptfds_copy) {
240 			errno = ENOMEM;
241 			goto out;
242 		}
243 	}
244 
245 	if (timeout) {
246 		timeval = z_user_alloc_from_copy((void *)timeout,
247 						 sizeof(struct zsock_timeval));
248 		if (!timeval) {
249 			errno = ENOMEM;
250 			goto out;
251 		}
252 	}
253 
254 	ret = z_impl_zsock_select(nfds, readfds_copy, writefds_copy,
255 				  exceptfds_copy, timeval);
256 
257 	if (ret >= 0) {
258 		if (readfds_copy) {
259 			z_user_to_copy((void *)readfds, readfds_copy,
260 				       sizeof(zsock_fd_set));
261 		}
262 
263 		if (writefds_copy) {
264 			z_user_to_copy((void *)writefds, writefds_copy,
265 				       sizeof(zsock_fd_set));
266 		}
267 
268 		if (exceptfds_copy) {
269 			z_user_to_copy((void *)exceptfds, exceptfds_copy,
270 				       sizeof(zsock_fd_set));
271 		}
272 	}
273 
274 out:
275 	k_free(timeval);
276 	k_free(readfds_copy);
277 	k_free(writefds_copy);
278 	k_free(exceptfds_copy);
279 
280 	return ret;
281 }
282 #include <syscalls/zsock_select_mrsh.c>
283 #endif
284