1/*
2 * Copyright 2017, 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(DATA61_BSD)
11 */
12
13#include <autoconf.h>
14#include <sel4camkes/gen_config.h>
15#include <assert.h>
16#include <errno.h>
17#include <fcntl.h>
18#include <limits.h>
19#include <stdarg.h>
20#include <string.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <unistd.h>
24
25#include <sel4/sel4.h>
26
27#include <sys/resource.h>
28#include <sys/mman.h>
29#include <sys/uio.h>
30
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <bits/syscall.h>
34#include <muslcsys/vsyscall.h>
35#include <muslcsys/io.h>
36
37#include <sel4utils/util.h>
38
39#include "sys_io.h"
40
41static muslcsys_syscall_t original_sys_close = NULL;
42static muslcsys_syscall_t original_sys_read = NULL;
43static muslcsys_syscall_t original_sys_write = NULL;
44
45int sock_close(int fd) __attribute__((weak));
46static long camkes_sys_close(va_list ap)
47{
48    va_list copy;
49    va_copy(copy, ap);
50    int fd = va_arg(ap, int);
51    if (sock_close && valid_fd(fd)) {
52        muslcsys_fd_t *fds =  get_fd_struct(fd);
53        if (fds->filetype == FILE_TYPE_SOCKET) {
54            sock_close(*(int *)fds->data);
55        }
56    }
57    long ret;
58    if (original_sys_close) {
59        ret = original_sys_close(copy);
60    } else {
61        ret = -ENOSYS;
62    }
63    va_end(copy);
64    return ret;
65}
66
67int sock_write(int sockfd, int count) __attribute__((weak));
68static long camkes_sys_write(va_list ap)
69{
70    va_list copy;
71    va_copy(copy, ap);
72    int fd = va_arg(ap, int);
73    void *buf = va_arg(ap, void *);
74    size_t count = va_arg(ap, size_t);
75
76    if (sock_write && sock_data && valid_fd(fd)) {
77        muslcsys_fd_t *fds = get_fd_struct(fd);
78        if (fds->filetype == FILE_TYPE_SOCKET) {
79            int sockfd = *(int *)fds->data;
80            ssize_t size = count > PAGE_SIZE_4K ? PAGE_SIZE_4K : count;
81            memcpy((char *)sock_data, buf, size);
82            return sock_write(sockfd, size);
83        }
84    }
85    long ret;
86    if (original_sys_write) {
87        ret = original_sys_write(copy);
88    } else {
89        // redirect to writev as a last resort
90        struct iovec io;
91        io.iov_base = buf;
92        io.iov_len = count;
93        ret = writev(fd, &io, 1);
94        // as the syscall implementation we expect to return the error directly and have
95        // our caller set errno or not. writev, however, is documented as putting its error
96        // code in errno. So if writev returns an error we need to get the error out of errno
97        // and return it up, so that it can ultimately get put back into errno
98        if (ret == -1) {
99            ret = errno;
100        }
101    }
102    va_end(copy);
103    return ret;
104}
105
106int sock_read(int sockfd, int count) __attribute__((weak));
107static long camkes_sys_read(va_list ap)
108{
109    va_list copy;
110    va_copy(copy, ap);
111    int fd = va_arg(ap, int);
112    void *buf = va_arg(ap, void *);
113    size_t count = va_arg(ap, size_t);
114    if (sock_read && sock_data && valid_fd(fd)) {
115        muslcsys_fd_t *fds = get_fd_struct(fd);
116        if (fds->filetype == FILE_TYPE_SOCKET) {
117            int sockfd = *(int *)fds->data;
118            int size = count > PAGE_SIZE_4K ? PAGE_SIZE_4K : count;
119            int ret = sock_read(sockfd, size);
120            memcpy(buf, (char *)sock_data, ret);
121            return ret;
122        }
123    }
124    long ret;
125    if (original_sys_read) {
126        ret = original_sys_read(copy);
127    } else {
128        ret = -ENOSYS;
129    }
130    va_end(copy);
131    return ret;
132}
133
134int sock_fcntl(int sockfd, int cmd, int val) __attribute__((weak));
135static long UNUSED camkes_sys_fcntl64(va_list ap)
136{
137    int fd = va_arg(ap, int);
138    int cmd = va_arg(ap, int);
139
140    int sockfd;
141    muslcsys_fd_t *fdt = get_fd_struct(fd);
142    if (fdt->filetype == FILE_TYPE_SOCKET && sock_fcntl) {
143        sockfd = *(int *)fdt->data;
144        long val = va_arg(ap, long);
145        return sock_fcntl(sockfd, cmd, val);
146    }
147
148    assert(!"sys_fcntl64 not implemented");
149    return -EINVAL;
150}
151
152void camkes_install_io_syscalls()
153{
154    original_sys_close = muslcsys_install_syscall(__NR_close, camkes_sys_close);
155    assert(original_sys_close);
156    original_sys_read = muslcsys_install_syscall(__NR_read, camkes_sys_read);
157    assert(original_sys_read);
158    original_sys_write = muslcsys_install_syscall(__NR_write, camkes_sys_write);
159#ifdef __NR_fcntl64
160    muslcsys_install_syscall(__NR_fcntl64, camkes_sys_fcntl64);
161#endif
162}
163