xref: /openbmc/qemu/bsd-user/freebsd/os-syscall.c (revision 770d8aba)
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 
45 /* I/O */
46 safe_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes);
47 safe_syscall4(ssize_t, pread, int, fd, void *, buf, size_t, nbytes, off_t,
48     offset);
49 safe_syscall3(ssize_t, readv, int, fd, const struct iovec *, iov, int, iovcnt);
50 safe_syscall4(ssize_t, preadv, int, fd, const struct iovec *, iov, int, iovcnt,
51     off_t, offset);
52 
53 safe_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes);
54 safe_syscall4(ssize_t, pwrite, int, fd, void *, buf, size_t, nbytes, off_t,
55     offset);
56 safe_syscall3(ssize_t, writev, int, fd, const struct iovec *, iov, int, iovcnt);
57 safe_syscall4(ssize_t, pwritev, int, fd, const struct iovec *, iov, int, iovcnt,
58     off_t, offset);
59 
60 void target_set_brk(abi_ulong new_brk)
61 {
62 }
63 
64 /*
65  * errno conversion.
66  */
67 abi_long get_errno(abi_long ret)
68 {
69     if (ret == -1) {
70         return -host_to_target_errno(errno);
71     } else {
72         return ret;
73     }
74 }
75 
76 int host_to_target_errno(int err)
77 {
78     /*
79      * All the BSDs have the property that the error numbers are uniform across
80      * all architectures for a given BSD, though they may vary between different
81      * BSDs.
82      */
83     return err;
84 }
85 
86 bool is_error(abi_long ret)
87 {
88     return (abi_ulong)ret >= (abi_ulong)(-4096);
89 }
90 
91 /*
92  * Unlocks a iovec. Unlike unlock_iovec, it assumes the tvec array itself is
93  * already locked from target_addr. It will be unlocked as well as all the iovec
94  * elements.
95  */
96 static void helper_unlock_iovec(struct target_iovec *target_vec,
97                                 abi_ulong target_addr, struct iovec *vec,
98                                 int count, int copy)
99 {
100     for (int i = 0; i < count; i++) {
101         abi_ulong base = tswapal(target_vec[i].iov_base);
102 
103         if (vec[i].iov_base) {
104             unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
105         }
106     }
107     unlock_user(target_vec, target_addr, 0);
108 }
109 
110 struct iovec *lock_iovec(int type, abi_ulong target_addr,
111         int count, int copy)
112 {
113     struct target_iovec *target_vec;
114     struct iovec *vec;
115     abi_ulong total_len, max_len;
116     int i;
117     int err = 0;
118 
119     if (count == 0) {
120         errno = 0;
121         return NULL;
122     }
123     if (count < 0 || count > IOV_MAX) {
124         errno = EINVAL;
125         return NULL;
126     }
127 
128     vec = g_try_new0(struct iovec, count);
129     if (vec == NULL) {
130         errno = ENOMEM;
131         return NULL;
132     }
133 
134     target_vec = lock_user(VERIFY_READ, target_addr,
135                            count * sizeof(struct target_iovec), 1);
136     if (target_vec == NULL) {
137         err = EFAULT;
138         goto fail2;
139     }
140 
141     max_len = 0x7fffffff & MIN(TARGET_PAGE_MASK, PAGE_MASK);
142     total_len = 0;
143 
144     for (i = 0; i < count; i++) {
145         abi_ulong base = tswapal(target_vec[i].iov_base);
146         abi_long len = tswapal(target_vec[i].iov_len);
147 
148         if (len < 0) {
149             err = EINVAL;
150             goto fail;
151         } else if (len == 0) {
152             /* Zero length pointer is ignored. */
153             vec[i].iov_base = 0;
154         } else {
155             vec[i].iov_base = lock_user(type, base, len, copy);
156             /*
157              * If the first buffer pointer is bad, this is a fault.  But
158              * subsequent bad buffers will result in a partial write; this is
159              * realized by filling the vector with null pointers and zero
160              * lengths.
161              */
162             if (!vec[i].iov_base) {
163                 if (i == 0) {
164                     err = EFAULT;
165                     goto fail;
166                 } else {
167                     /*
168                      * Fail all the subsequent addresses, they are already
169                      * zero'd.
170                      */
171                     goto out;
172                 }
173             }
174             if (len > max_len - total_len) {
175                 len = max_len - total_len;
176             }
177         }
178         vec[i].iov_len = len;
179         total_len += len;
180     }
181 out:
182     unlock_user(target_vec, target_addr, 0);
183     return vec;
184 
185 fail:
186     helper_unlock_iovec(target_vec, target_addr, vec, i, copy);
187 fail2:
188     g_free(vec);
189     errno = err;
190     return NULL;
191 }
192 
193 void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
194         int count, int copy)
195 {
196     struct target_iovec *target_vec;
197 
198     target_vec = lock_user(VERIFY_READ, target_addr,
199                            count * sizeof(struct target_iovec), 1);
200     if (target_vec) {
201         helper_unlock_iovec(target_vec, target_addr, vec, count, copy);
202     }
203 
204     g_free(vec);
205 }
206 
207 /*
208  * All errnos that freebsd_syscall() returns must be -TARGET_<errcode>.
209  */
210 static abi_long freebsd_syscall(void *cpu_env, int num, abi_long arg1,
211                                 abi_long arg2, abi_long arg3, abi_long arg4,
212                                 abi_long arg5, abi_long arg6, abi_long arg7,
213                                 abi_long arg8)
214 {
215     abi_long ret;
216 
217     switch (num) {
218 
219         /*
220          * File system calls.
221          */
222     case TARGET_FREEBSD_NR_read: /* read(2) */
223         ret = do_bsd_read(arg1, arg2, arg3);
224         break;
225 
226     case TARGET_FREEBSD_NR_pread: /* pread(2) */
227         ret = do_bsd_pread(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
228         break;
229 
230     case TARGET_FREEBSD_NR_readv: /* readv(2) */
231         ret = do_bsd_readv(arg1, arg2, arg3);
232         break;
233 
234     case TARGET_FREEBSD_NR_preadv: /* preadv(2) */
235         ret = do_bsd_preadv(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
236 
237     case TARGET_FREEBSD_NR_write: /* write(2) */
238         ret = do_bsd_write(arg1, arg2, arg3);
239         break;
240 
241     case TARGET_FREEBSD_NR_pwrite: /* pwrite(2) */
242         ret = do_bsd_pwrite(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
243         break;
244 
245     case TARGET_FREEBSD_NR_writev: /* writev(2) */
246         ret = do_bsd_writev(arg1, arg2, arg3);
247         break;
248 
249     case TARGET_FREEBSD_NR_pwritev: /* pwritev(2) */
250         ret = do_bsd_pwritev(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6);
251         break;
252 
253     default:
254         qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
255         ret = -TARGET_ENOSYS;
256         break;
257     }
258 
259     return ret;
260 }
261 
262 /*
263  * do_freebsd_syscall() should always have a single exit point at the end so
264  * that actions, such as logging of syscall results, can be performed. This
265  * as a wrapper around freebsd_syscall() so that actually happens. Since
266  * that is a singleton, modern compilers will inline it anyway...
267  */
268 abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
269                             abi_long arg2, abi_long arg3, abi_long arg4,
270                             abi_long arg5, abi_long arg6, abi_long arg7,
271                             abi_long arg8)
272 {
273     CPUState *cpu = env_cpu(cpu_env);
274     int ret;
275 
276     trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
277     if (do_strace) {
278         print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
279     }
280 
281     ret = freebsd_syscall(cpu_env, num, arg1, arg2, arg3, arg4, arg5, arg6,
282                           arg7, arg8);
283     if (do_strace) {
284         print_freebsd_syscall_ret(num, ret);
285     }
286     trace_guest_user_syscall_ret(cpu, num, ret);
287 
288     return ret;
289 }
290 
291 void syscall_init(void)
292 {
293 }
294