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