xref: /openbmc/qemu/bsd-user/freebsd/os-syscall.c (revision 9554d330)
1 /*
2  *  BSD syscalls
3  *
4  *  Copyright (c) 2003-2008 Fabrice Bellard
5  *  Copyright (c) 2013-2014 Stacey D. Son
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /*
22  * We need the FreeBSD "legacy" definitions. Rust needs the FreeBSD 11 system
23  * calls since it doesn't use libc at all, so we have to emulate that despite
24  * FreeBSD 11 being EOL'd.
25  */
26 #define _WANT_FREEBSD11_STAT
27 #define _WANT_FREEBSD11_STATFS
28 #define _WANT_FREEBSD11_DIRENT
29 #define _WANT_KERNEL_ERRNO
30 #define _WANT_SEMUN
31 #include "qemu/osdep.h"
32 #include "qemu/cutils.h"
33 #include "qemu/path.h"
34 #include <sys/syscall.h>
35 #include <sys/param.h>
36 #include <sys/sysctl.h>
37 #include <utime.h>
38 
39 #include "qemu.h"
40 #include "signal-common.h"
41 #include "user/syscall-trace.h"
42 
43 #include "bsd-file.h"
44 #include "bsd-proc.h"
45 
46 /* I/O */
47 safe_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes);
48 safe_syscall4(ssize_t, pread, int, fd, void *, buf, size_t, nbytes, off_t,
49     offset);
50 safe_syscall3(ssize_t, readv, int, fd, const struct iovec *, iov, int, iovcnt);
51 safe_syscall4(ssize_t, preadv, int, fd, const struct iovec *, iov, int, iovcnt,
52     off_t, offset);
53 
54 safe_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes);
55 safe_syscall4(ssize_t, pwrite, int, fd, void *, buf, size_t, nbytes, off_t,
56     offset);
57 safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt);
58 safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt,
59     off_t, offset);
60 
61 void target_set_brk(abi_ulong new_brk)
62 {
63 }
64 
65 /*
66  * errno conversion.
67  */
68 abi_long get_errno(abi_long ret)
69 {
70     if (ret == -1) {
71         return -host_to_target_errno(errno);
72     } else {
73         return ret;
74     }
75 }
76 
77 int host_to_target_errno(int err)
78 {
79     /*
80      * All the BSDs have the property that the error numbers are uniform across
81      * all architectures for a given BSD, though they may vary between different
82      * BSDs.
83      */
84     return err;
85 }
86 
87 bool is_error(abi_long ret)
88 {
89     return (abi_ulong)ret >= (abi_ulong)(-4096);
90 }
91 
92 /*
93  * Unlocks a iovec. Unlike unlock_iovec, it assumes the tvec array itself is
94  * already locked from target_addr. It will be unlocked as well as all the iovec
95  * elements.
96  */
97 static void helper_unlock_iovec(struct target_iovec *target_vec,
98                                 abi_ulong target_addr, struct iovec *vec,
99                                 int count, int copy)
100 {
101     for (int i = 0; i < count; i++) {
102         abi_ulong base = tswapal(target_vec[i].iov_base);
103 
104         if (vec[i].iov_base) {
105             unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
106         }
107     }
108     unlock_user(target_vec, target_addr, 0);
109 }
110 
111 struct iovec *lock_iovec(int type, abi_ulong target_addr,
112         int count, int copy)
113 {
114     struct target_iovec *target_vec;
115     struct iovec *vec;
116     abi_ulong total_len, max_len;
117     int i;
118     int err = 0;
119 
120     if (count == 0) {
121         errno = 0;
122         return NULL;
123     }
124     if (count < 0 || count > IOV_MAX) {
125         errno = EINVAL;
126         return NULL;
127     }
128 
129     vec = g_try_new0(struct iovec, count);
130     if (vec == NULL) {
131         errno = ENOMEM;
132         return NULL;
133     }
134 
135     target_vec = lock_user(VERIFY_READ, target_addr,
136                            count * sizeof(struct target_iovec), 1);
137     if (target_vec == NULL) {
138         err = EFAULT;
139         goto fail2;
140     }
141 
142     max_len = 0x7fffffff & MIN(TARGET_PAGE_MASK, PAGE_MASK);
143     total_len = 0;
144 
145     for (i = 0; i < count; i++) {
146         abi_ulong base = tswapal(target_vec[i].iov_base);
147         abi_long len = tswapal(target_vec[i].iov_len);
148 
149         if (len < 0) {
150             err = EINVAL;
151             goto fail;
152         } else if (len == 0) {
153             /* Zero length pointer is ignored. */
154             vec[i].iov_base = 0;
155         } else {
156             vec[i].iov_base = lock_user(type, base, len, copy);
157             /*
158              * If the first buffer pointer is bad, this is a fault.  But
159              * subsequent bad buffers will result in a partial write; this is
160              * realized by filling the vector with null pointers and zero
161              * lengths.
162              */
163             if (!vec[i].iov_base) {
164                 if (i == 0) {
165                     err = EFAULT;
166                     goto fail;
167                 } else {
168                     /*
169                      * Fail all the subsequent addresses, they are already
170                      * zero'd.
171                      */
172                     goto out;
173                 }
174             }
175             if (len > max_len - total_len) {
176                 len = max_len - total_len;
177             }
178         }
179         vec[i].iov_len = len;
180         total_len += len;
181     }
182 out:
183     unlock_user(target_vec, target_addr, 0);
184     return vec;
185 
186 fail:
187     helper_unlock_iovec(target_vec, target_addr, vec, i, copy);
188 fail2:
189     g_free(vec);
190     errno = err;
191     return NULL;
192 }
193 
194 void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
195         int count, int copy)
196 {
197     struct target_iovec *target_vec;
198 
199     target_vec = lock_user(VERIFY_READ, target_addr,
200                            count * sizeof(struct target_iovec), 1);
201     if (target_vec) {
202         helper_unlock_iovec(target_vec, target_addr, vec, count, copy);
203     }
204 
205     g_free(vec);
206 }
207 
208 /*
209  * All errnos that freebsd_syscall() returns must be -TARGET_<errcode>.
210  */
211 static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
212                                 abi_long arg2, abi_long arg3, abi_long arg4,
213                                 abi_long arg5, abi_long arg6, abi_long arg7,
214                                 abi_long arg8)
215 {
216     abi_long ret;
217 
218     switch (num) {
219         /*
220          * process system calls
221          */
222     case TARGET_FREEBSD_NR_exit: /* exit(2) */
223         ret = do_bsd_exit(cpu_env, arg1);
224         break;
225 
226         /*
227          * File system calls.
228          */
229     case TARGET_FREEBSD_NR_read: /* read(2) */
230         ret = do_bsd_read(arg1, arg2, arg3);
231         break;
232 
233     case TARGET_FREEBSD_NR_pread: /* pread(2) */
234         ret = do_bsd_pread(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
235         break;
236 
237     case TARGET_FREEBSD_NR_readv: /* readv(2) */
238         ret = do_bsd_readv(arg1, arg2, arg3);
239         break;
240 
241     case TARGET_FREEBSD_NR_preadv: /* preadv(2) */
242         ret = do_bsd_preadv(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
243 
244     case TARGET_FREEBSD_NR_write: /* write(2) */
245         ret = do_bsd_write(arg1, arg2, arg3);
246         break;
247 
248     case TARGET_FREEBSD_NR_pwrite: /* pwrite(2) */
249         ret = do_bsd_pwrite(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
250         break;
251 
252     case TARGET_FREEBSD_NR_writev: /* writev(2) */
253         ret = do_bsd_writev(arg1, arg2, arg3);
254         break;
255 
256     case TARGET_FREEBSD_NR_pwritev: /* pwritev(2) */
257         ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
258         break;
259 
260     default:
261         qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
262         ret = -TARGET_ENOSYS;
263         break;
264     }
265 
266     return ret;
267 }
268 
269 /*
270  * do_freebsd_syscall() should always have a single exit point at the end so
271  * that actions, such as logging of syscall results, can be performed. This
272  * as a wrapper around freebsd_syscall() so that actually happens. Since
273  * that is a singleton, modern compilers will inline it anyway...
274  */
275 abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
276                             abi_long arg2, abi_long arg3, abi_long arg4,
277                             abi_long arg5, abi_long arg6, abi_long arg7,
278                             abi_long arg8)
279 {
280     CPUState *cpu = env_cpu(cpu_env);
281     int ret;
282 
283     trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
284     if (do_strace) {
285         print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
286     }
287 
288     ret = freebsd_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6,
289                           arg7, arg8);
290     if (do_strace) {
291         print_freebsd_syscall_ret(num, ret);
292     }
293     trace_guest_user_syscall_ret(cpu, num, ret);
294 
295     return ret;
296 }
297 
298 void syscall_init(void)
299 {
300 }
301