blob: 2839ed47c055bf8a1758b36f1dab266788e77814 [file] [log] [blame]
Paul Sokolovskyf484bba2018-10-08 13:55:03 +03001/*
2 * Copyright (c) 2018 Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7/**
8 * @file
9 * @brief File descriptor table
10 *
11 * This file provides generic file descriptor table implementation, suitable
12 * for any I/O object implementing POSIX I/O semantics (i.e. read/write +
13 * aux operations).
14 */
15
16#include <errno.h>
Paul Sokolovsky13b38ed2018-12-11 17:48:47 +030017#include <fcntl.h>
Paul Sokolovskyf484bba2018-10-08 13:55:03 +030018#include <kernel.h>
Anas Nashif8be9f5d2019-06-26 10:33:43 -040019#include <sys/fdtable.h>
Anas Nashifd2225532019-06-26 10:33:53 -040020#include <sys/speculation.h>
Andrew Boie87480cd2020-05-29 14:52:26 -070021#include <syscall_handler.h>
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030022#include <sys/atomic.h>
Paul Sokolovskyf484bba2018-10-08 13:55:03 +030023
24struct fd_entry {
25 void *obj;
26 const struct fd_op_vtable *vtable;
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030027 atomic_t refcount;
Paul Sokolovskyf484bba2018-10-08 13:55:03 +030028};
29
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +030030#ifdef CONFIG_POSIX_API
Paul Sokolovsky8f690e22018-11-08 10:06:28 +030031static const struct fd_op_vtable stdinout_fd_op_vtable;
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +030032#endif
33
Andrew Boie41f60112019-01-31 15:53:24 -080034static struct fd_entry fdtable[CONFIG_POSIX_MAX_FDS] = {
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +030035#ifdef CONFIG_POSIX_API
36 /*
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030037 * Predefine entries for stdin/stdout/stderr.
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +030038 */
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030039 {
40 /* STDIN */
41 .vtable = &stdinout_fd_op_vtable,
42 .refcount = ATOMIC_INIT(1)
43 },
44 {
45 /* STDOUT */
46 .vtable = &stdinout_fd_op_vtable,
47 .refcount = ATOMIC_INIT(1)
48 },
49 {
50 /* STDERR */
51 .vtable = &stdinout_fd_op_vtable,
52 .refcount = ATOMIC_INIT(1)
53 },
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +030054#endif
55};
Paul Sokolovskyf484bba2018-10-08 13:55:03 +030056
57static K_MUTEX_DEFINE(fdtable_lock);
58
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030059static int z_fd_ref(int fd)
60{
61 return atomic_inc(&fdtable[fd].refcount) + 1;
62}
63
64static int z_fd_unref(int fd)
65{
Grzegorz Kostkacccb1902020-09-07 13:11:15 +020066 atomic_val_t old_rc;
67
68 /* Reference counter must be checked to avoid decrement refcount below
69 * zero causing file descriptor leak. Loop statement below executes
70 * atomic decrement if refcount value is grater than zero. Otherwise,
71 * refcount is not going to be written.
72 */
73 do {
74 old_rc = atomic_get(&fdtable[fd].refcount);
75 if (!old_rc) {
76 return 0;
77 }
78 } while (!atomic_cas(&fdtable[fd].refcount, old_rc, old_rc - 1));
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030079
80 if (old_rc != 1) {
81 return old_rc - 1;
82 }
83
84 fdtable[fd].obj = NULL;
85 fdtable[fd].vtable = NULL;
86
87 return 0;
88}
89
Paul Sokolovskyf484bba2018-10-08 13:55:03 +030090static int _find_fd_entry(void)
91{
92 int fd;
93
94 for (fd = 0; fd < ARRAY_SIZE(fdtable); fd++) {
Jukka Rissanen8b578bd2020-06-02 15:18:42 +030095 if (!atomic_get(&fdtable[fd].refcount)) {
Paul Sokolovskyf484bba2018-10-08 13:55:03 +030096 return fd;
97 }
98 }
99
100 errno = ENFILE;
101 return -1;
102}
103
104static int _check_fd(int fd)
105{
Andrew Boie74164572019-03-08 10:53:48 -0800106 if (fd < 0 || fd >= ARRAY_SIZE(fdtable)) {
107 errno = EBADF;
108 return -1;
109 }
110
111 fd = k_array_index_sanitize(fd, ARRAY_SIZE(fdtable));
112
Jukka Rissanen8b578bd2020-06-02 15:18:42 +0300113 if (!atomic_get(&fdtable[fd].refcount)) {
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300114 errno = EBADF;
115 return -1;
116 }
117
118 return 0;
119}
120
121void *z_get_fd_obj(int fd, const struct fd_op_vtable *vtable, int err)
122{
123 struct fd_entry *fd_entry;
124
125 if (_check_fd(fd) < 0) {
126 return NULL;
127 }
128
129 fd_entry = &fdtable[fd];
130
131 if (vtable != NULL && fd_entry->vtable != vtable) {
132 errno = err;
133 return NULL;
134 }
135
136 return fd_entry->obj;
137}
138
Robert Lubos03df2bb2018-11-21 15:14:28 +0100139void *z_get_fd_obj_and_vtable(int fd, const struct fd_op_vtable **vtable)
140{
141 struct fd_entry *fd_entry;
142
143 if (_check_fd(fd) < 0) {
144 return NULL;
145 }
146
147 fd_entry = &fdtable[fd];
148 *vtable = fd_entry->vtable;
149
150 return fd_entry->obj;
151}
152
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300153int z_reserve_fd(void)
154{
155 int fd;
156
157 (void)k_mutex_lock(&fdtable_lock, K_FOREVER);
158
159 fd = _find_fd_entry();
160 if (fd >= 0) {
161 /* Mark entry as used, z_finalize_fd() will fill it in. */
Vincent Wan0e436ed2020-08-21 14:37:39 -0700162 (void)z_fd_ref(fd);
Jukka Rissanen8b578bd2020-06-02 15:18:42 +0300163 fdtable[fd].obj = NULL;
164 fdtable[fd].vtable = NULL;
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300165 }
166
167 k_mutex_unlock(&fdtable_lock);
168
169 return fd;
170}
171
172void z_finalize_fd(int fd, void *obj, const struct fd_op_vtable *vtable)
173{
174 /* Assumes fd was already bounds-checked. */
Andrew Boie87480cd2020-05-29 14:52:26 -0700175#ifdef CONFIG_USERSPACE
176 /* descriptor context objects are inserted into the table when they
177 * are ready for use. Mark the object as initialized and grant the
178 * caller (and only the caller) access.
179 *
180 * This call is a no-op if obj is invalid or points to something
181 * not a kernel object.
182 */
183 z_object_recycle(obj);
184#endif
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300185 fdtable[fd].obj = obj;
186 fdtable[fd].vtable = vtable;
187}
188
189void z_free_fd(int fd)
190{
191 /* Assumes fd was already bounds-checked. */
Jukka Rissanen8b578bd2020-06-02 15:18:42 +0300192 (void)z_fd_unref(fd);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300193}
194
195int z_alloc_fd(void *obj, const struct fd_op_vtable *vtable)
196{
197 int fd;
198
199 fd = z_reserve_fd();
200 if (fd >= 0) {
201 z_finalize_fd(fd, obj, vtable);
202 }
203
204 return fd;
205}
206
207#ifdef CONFIG_POSIX_API
208
209ssize_t read(int fd, void *buf, size_t sz)
210{
211 if (_check_fd(fd) < 0) {
212 return -1;
213 }
214
215 return fdtable[fd].vtable->read(fdtable[fd].obj, buf, sz);
216}
Paul Sokolovskyd01f75b2019-02-28 13:55:43 +0300217FUNC_ALIAS(read, _read, ssize_t);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300218
219ssize_t write(int fd, const void *buf, size_t sz)
220{
221 if (_check_fd(fd) < 0) {
222 return -1;
223 }
224
225 return fdtable[fd].vtable->write(fdtable[fd].obj, buf, sz);
226}
Paul Sokolovskyd01f75b2019-02-28 13:55:43 +0300227FUNC_ALIAS(write, _write, ssize_t);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300228
229int close(int fd)
230{
231 int res;
232
233 if (_check_fd(fd) < 0) {
234 return -1;
235 }
236
Jukka Rissanen2ed6b6a2020-08-11 11:43:51 +0300237 res = fdtable[fd].vtable->close(fdtable[fd].obj);
238
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300239 z_free_fd(fd);
240
241 return res;
242}
Paul Sokolovskyd01f75b2019-02-28 13:55:43 +0300243FUNC_ALIAS(close, _close, int);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300244
245int fsync(int fd)
246{
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300247 if (_check_fd(fd) < 0) {
248 return -1;
249 }
250
Paul Sokolovsky13b38ed2018-12-11 17:48:47 +0300251 return z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, ZFD_IOCTL_FSYNC);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300252}
253
254off_t lseek(int fd, off_t offset, int whence)
255{
256 if (_check_fd(fd) < 0) {
257 return -1;
258 }
259
Paul Sokolovsky13b38ed2018-12-11 17:48:47 +0300260 return z_fdtable_call_ioctl(fdtable[fd].vtable, fdtable[fd].obj, ZFD_IOCTL_LSEEK,
261 offset, whence);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300262}
Paul Sokolovskyd01f75b2019-02-28 13:55:43 +0300263FUNC_ALIAS(lseek, _lseek, off_t);
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300264
Paul Sokolovsky4a04ed22018-12-11 17:53:56 +0300265int ioctl(int fd, unsigned long request, ...)
266{
267 va_list args;
268 int res;
269
270 if (_check_fd(fd) < 0) {
271 return -1;
272 }
273
274 va_start(args, request);
275 res = fdtable[fd].vtable->ioctl(fdtable[fd].obj, request, args);
276 va_end(args);
277
278 return res;
279}
280
281int fcntl(int fd, int cmd, ...)
282{
283 va_list args;
284 int res;
285
286 if (_check_fd(fd) < 0) {
287 return -1;
288 }
289
290 /* Handle fdtable commands. */
291 switch (cmd) {
292 case F_DUPFD:
293 /* Not implemented so far. */
294 errno = EINVAL;
295 return -1;
296 }
297
298 /* The rest of commands are per-fd, handled by ioctl vmethod. */
299 va_start(args, cmd);
300 res = fdtable[fd].vtable->ioctl(fdtable[fd].obj, cmd, args);
301 va_end(args);
302
303 return res;
304}
305
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +0300306/*
307 * fd operations for stdio/stdout/stderr
308 */
309
Patrik Flykt4344e272019-03-08 14:19:05 -0700310int z_impl_zephyr_write_stdout(const char *buf, int nbytes);
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +0300311
312static ssize_t stdinout_read_vmeth(void *obj, void *buffer, size_t count)
313{
314 return 0;
315}
316
317static ssize_t stdinout_write_vmeth(void *obj, const void *buffer, size_t count)
318{
Paul Sokolovsky79ea6132018-10-22 16:56:51 +0300319#if defined(CONFIG_BOARD_NATIVE_POSIX)
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +0300320 return write(1, buffer, count);
Paul Sokolovsky79ea6132018-10-22 16:56:51 +0300321#elif defined(CONFIG_NEWLIB_LIBC)
Patrik Flykt4344e272019-03-08 14:19:05 -0700322 return z_impl_zephyr_write_stdout(buffer, count);
Paul Sokolovsky79ea6132018-10-22 16:56:51 +0300323#else
324 return 0;
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +0300325#endif
326}
327
Paul Sokolovsky13b38ed2018-12-11 17:48:47 +0300328static int stdinout_ioctl_vmeth(void *obj, unsigned int request, va_list args)
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +0300329{
330 errno = EINVAL;
331 return -1;
332}
333
334
Paul Sokolovsky8f690e22018-11-08 10:06:28 +0300335static const struct fd_op_vtable stdinout_fd_op_vtable = {
Paul Sokolovsky65a33bd2018-10-18 22:01:02 +0300336 .read = stdinout_read_vmeth,
337 .write = stdinout_write_vmeth,
338 .ioctl = stdinout_ioctl_vmeth,
339};
340
Paul Sokolovskyf484bba2018-10-08 13:55:03 +0300341#endif /* CONFIG_POSIX_API */