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