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