1/*
2 * Copyright 2016, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(D61_BSD)
11 */
12
13#include <refos/error.h>
14#include <refos-io/internal_state.h>
15#include <refos-io/ipc_state.h>
16#include <refos-io/filetable.h>
17#include <refos-util/init.h>
18#include <refos-rpc/data_client.h>
19#include <refos-rpc/data_client_helper.h>
20#include <utils/arith.h>
21#include <autoconf.h>
22
23#include <stdio.h>
24#include <sys/uio.h>
25#include <limits.h>
26#include <errno.h>
27#include <sel4/sel4.h>
28#include <stdarg.h>
29#include <fcntl.h>
30#include <refos-util/dprintf.h>
31
32#define STDIN_FD 0
33#define STDOUT_FD 1
34#define STDERR_FD 2
35
36#define REFOS_SYSIO_MAX_PATHLEN 256
37
38static size_t
39sys_platform_stdout_write(void *data, size_t count)
40{
41    char *cdata = data;
42
43#if defined(SEL4_DEBUG_KERNEL) && defined(CONFIG_REFOS_SYS_FORCE_DEBUGPUTCHAR)
44    for (size_t i = 0; i < count; i++) {
45        seL4_DebugPutChar(cdata[i]);
46    }
47#else
48
49    if (refosIOState.stdioWriteOverride != NULL) {
50        /* Use overridden write function. */
51        return refosIOState.stdioWriteOverride(data, count);
52    }
53
54    /* Use serial dataspace on Console server. */
55    if (refosIOState.stdioDataspace && refosIOState.stdioSession.serverSession) {
56        refosio_internal_save_IPC_buffer();
57        for (size_t i = 0; i < count;) {
58            int c = MIN(REFOS_DEFAULT_DSPACE_IPC_MAXLEN, count - i);
59            int n = data_write(refosIOState.stdioSession.serverSession, refosIOState.stdioDataspace,
60                               0, &cdata[i], c);
61            if (!n) {
62                /* An error occured. */
63                refosio_internal_restore_IPC_buffer();
64                return i;
65            }
66
67            i += n;
68        }
69        refosio_internal_restore_IPC_buffer();
70    }
71
72#endif
73
74    return count;
75}
76
77static size_t
78sys_platform_stdin_read(void *data, size_t count)
79{
80    assert(data && count);
81    int c = refos_getc();
82    if (c < 0) {
83        return 0;
84    }
85    char cc = (char) c;
86    memcpy(data, &cc, sizeof(char));
87    return sizeof(char);
88}
89
90/* Writev syscall implementation for muslc. Only implemented for stdin and stdout. */
91static long
92_sys_writev(int fildes, struct iovec *iov, int iovcnt)
93{
94    long long sum = 0;
95    ssize_t ret = 0;
96
97    /* The iovcnt argument is valid if greater than 0 and less than or equal to IOV_MAX. */
98    if (iovcnt <= 0 || iovcnt > IOV_MAX)
99        return -EINVAL;
100
101    /* The sum of iov_len is valid if less than or equal to SSIZE_MAX i.e. cannot overflow
102       a ssize_t. */
103    for (int i = 0; i < iovcnt; i++) {
104        if (iov[i].iov_len < 0)
105            return -EINVAL;
106
107        sum += (long long)iov[i].iov_len;
108        if (sum > SSIZE_MAX)
109            return -EINVAL;
110    }
111
112    /* If all the iov_len members in the array are 0, return 0. */
113    if (!sum)
114        return 0;
115
116    /* Write the buffer to console if the fd is for stdout or stderr. */
117    if (fildes == STDOUT_FD || fildes == STDERR_FD) {
118        for (int i = 0; i < iovcnt; i++) {
119            ret += sys_platform_stdout_write(iov[i].iov_base, iov[i].iov_len);
120        }
121    } else if (fildes == STDIN_FD) {
122        /* Can't write to stdin. */
123        assert(!"Can't write to stdin.");
124        return -EACCES;
125    } else {
126        for (int i = 0; i < iovcnt; i++) {
127            if (iov[i].iov_len == 0) continue;
128            int offset = 0;
129
130            while (offset < iov[i].iov_len) {
131                int nc = filetable_write(&refosIOState.fdTable, fildes, iov[i].iov_base + offset,
132                                         iov[i].iov_len - offset);
133                if (nc >= 0) {
134                    assert(nc <= iov[i].iov_len - offset);
135                    offset += nc;
136                    ret += nc;
137                } else {
138                    ret = -EFAULT;
139                    break;
140                }
141            }
142
143            if (ret < 0) {
144                break;
145            }
146        }
147    }
148
149    return ret;
150}
151
152long
153sys_writev(va_list ap)
154{
155    int fildes = va_arg(ap, int);
156    struct iovec *iov = va_arg(ap, struct iovec *);
157    int iovcnt = va_arg(ap, int);
158    return _sys_writev(fildes, iov, iovcnt);
159}
160
161long
162sys_write(va_list ap) {
163    int fildes = va_arg(ap, int);
164    void* buf = va_arg(ap, void*);
165    size_t count = va_arg(ap, size_t);
166
167    struct iovec iov = {
168        .iov_base = buf,
169        .iov_len = count
170    };
171
172    return _sys_writev(fildes, &iov, 1);
173}
174
175static long
176_sys_readv(int fildes, struct iovec *iov, int iovcnt)
177{
178    long long sum = 0;
179    ssize_t ret = 0;
180
181    /* The iovcnt argument is valid if greater than 0 and less than or equal to IOV_MAX. */
182    if (iovcnt <= 0 || iovcnt > IOV_MAX)
183        return -EINVAL;
184
185    /* The sum of iov_len is valid if less than or equal to SSIZE_MAX i.e. cannot overflow
186       a ssize_t. */
187    for (int i = 0; i < iovcnt; i++) {
188        if (iov[i].iov_len < 0)
189            return -EINVAL;
190
191        sum += (long long)iov[i].iov_len;
192        if (sum > SSIZE_MAX)
193            return -EINVAL;
194    }
195
196    /* If all the iov_len members in the array are 0, return 0. */
197    if (!sum)
198        return 0;
199
200    /* Read the iov buffers. */
201    if (fildes == STDIN_FD) {
202        /* Read from STDIN. */
203        for (int i = 0; i < iovcnt; i++) {
204            if (iov[i].iov_len == 0) continue;
205            ret += sys_platform_stdin_read(iov[i].iov_base, iov[i].iov_len);
206            break;
207        }
208        return ret;
209    }
210
211    /* Read from dataspace file. */
212    for (int i = 0; i < iovcnt; i++) {
213        if (iov[i].iov_len == 0) continue;
214
215        int nc = filetable_read(&refosIOState.fdTable, fildes, iov[i].iov_base, iov[i].iov_len);
216        if (nc < 0) {
217            return -1;
218        } else if (nc < iov[i].iov_len) {
219            ret += nc;
220            break;
221        }
222        ret += nc;
223    }
224
225    return ret;
226}
227
228long
229sys_readv(va_list ap)
230{
231    int fildes = va_arg(ap, int);
232    struct iovec *iov = va_arg(ap, struct iovec *);
233    int iovcnt = va_arg(ap, int);
234    return _sys_readv(fildes, iov, iovcnt);
235}
236
237long
238sys_read(va_list ap) {
239    int fildes = va_arg(ap, int);
240    void* buf = va_arg(ap, void*);
241    size_t count = va_arg(ap, size_t);
242
243    struct iovec iov = {
244        .iov_base = buf,
245        .iov_len = count
246    };
247
248    return _sys_readv(fildes, &iov, 1);
249}
250
251long
252sys_open(va_list ap)
253{
254    char *pathname = va_arg(ap, char*);
255    int flags = va_arg(ap, int);
256    int fd = -1;
257    static char tempBufferPath[REFOS_SYSIO_MAX_PATHLEN];
258
259    /* Handle the PWD environment variable. */
260    char *pwd = getenv("PWD");
261    if (pwd && strlen(pwd) > 0) {
262        snprintf(tempBufferPath, REFOS_SYSIO_MAX_PATHLEN, "%s%s", getenv("PWD"), pathname);
263        pathname = tempBufferPath;
264    }
265
266    /* Open dataspace file. */
267    fd = filetable_dspace_open(&refosIOState.fdTable, pathname, flags, 0, 0x1000);
268    switch (ROS_ERRNO()) {
269        case ESUCCESS: break;
270        case EINVALID: return -EFAULT;
271        case EINVALIDPARAM: return -EACCES;
272        case EFILENOTFOUND: return -EMFILE;
273        case ESERVERNOTFOUND: return -EMFILE;
274        default: return -EFAULT;
275    }
276    assert(fd);
277
278    return fd;
279}
280
281long
282_sys_lseek(int fildes, off_t offset, int whence)
283{
284    int newOffset = (int) offset;
285    if (fildes == STDOUT_FD || fildes == STDERR_FD || fildes == STDIN_FD) {
286        /* lseek for STDOUT / STDIN / STDERR makes no sense. */
287        return newOffset;
288    }
289
290    /* Perform lseek on filetable. */
291    int error = filetable_lseek(&refosIOState.fdTable, fildes, &newOffset, whence);
292    if (error != ESUCCESS) {
293        return -EFAULT;
294    }
295    return newOffset;
296}
297
298long
299sys_lseek(va_list ap)
300{
301    int fildes = va_arg(ap, int);
302    off_t offset = va_arg(ap, int);
303    int whence = va_arg(ap, int);
304    return _sys_lseek(fildes, offset , whence);
305}
306
307long
308sys__llseek(va_list ap)
309{
310    int fildes = va_arg(ap, int);
311    unsigned long offset_high = va_arg(ap, unsigned long);
312    unsigned long offset_low = va_arg(ap, unsigned long);
313    off_t *result = va_arg(ap, off_t*);
314    int whence = va_arg(ap, int);
315    off_t seek_distance;
316
317    if (!result) {
318        return -1;
319    }
320
321    int64_t seek_distance64 = (int64_t)(((uint64_t)offset_high << 32) | (uint64_t)offset_low);
322    if (seek_distance64 > (int64_t)INT_MAX || seek_distance64 < (int64_t)INT_MIN) {
323        /* This number cannot be represented by signed 32-bit integers, so return error.
324           If this ever presents a problem, consider extending this to use 64-bit integers. */
325        return -1;
326    }
327
328    seek_distance = (off_t)seek_distance64;
329
330    (*result) = (off_t) _sys_lseek(fildes, seek_distance, whence);
331    return 0;
332}
333
334long
335sys_ioctl(va_list ap)
336{
337    /* muslc does some ioctl to stdout, so just allow these to silently go through */
338    return 0;
339}
340
341long
342sys_close(va_list ap)
343{
344    int fildes = va_arg(ap, int);
345    if (fildes == STDOUT_FD || fildes == STDERR_FD || fildes == STDIN_FD) {
346        /* close for STDOUT / STDIN / STDERR makes no sense. */
347        return 0;
348    }
349
350    /* Perform close on filetable. */
351    int error = filetable_close(&refosIOState.fdTable, fildes);
352    if (error != ESUCCESS) {
353        return -EIO;
354    }
355    return 0;
356}
357