1 /*
2  *  BSD syscalls
3  *
4  *  Copyright (c) 2003 - 2008 Fabrice Bellard
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 #include "qemu/osdep.h"
20 #include "qemu/cutils.h"
21 #include "qemu/path.h"
22 #include <sys/syscall.h>
23 #include <sys/param.h>
24 #include <sys/sysctl.h>
25 #include <utime.h>
26 
27 #include "qemu.h"
28 #include "qemu-common.h"
29 #include "user/syscall-trace.h"
30 
31 //#define DEBUG
32 
33 static abi_ulong target_brk;
34 static abi_ulong target_original_brk;
35 
get_errno(abi_long ret)36 abi_long get_errno(abi_long ret)
37 {
38     if (ret == -1) {
39         /* XXX need to translate host -> target errnos here */
40         return -(errno);
41     }
42     return ret;
43 }
44 
45 #define target_to_host_bitmask(x, tbl) (x)
46 
is_error(abi_long ret)47 bool is_error(abi_long ret)
48 {
49     return (abi_ulong)ret >= (abi_ulong)(-4096);
50 }
51 
target_set_brk(abi_ulong new_brk)52 void target_set_brk(abi_ulong new_brk)
53 {
54     target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk);
55 }
56 
57 /* do_obreak() must return target errnos. */
do_obreak(abi_ulong new_brk)58 static abi_long do_obreak(abi_ulong new_brk)
59 {
60     abi_ulong brk_page;
61     abi_long mapped_addr;
62     int new_alloc_size;
63 
64     if (!new_brk)
65         return 0;
66     if (new_brk < target_original_brk)
67         return -TARGET_EINVAL;
68 
69     brk_page = HOST_PAGE_ALIGN(target_brk);
70 
71     /* If the new brk is less than this, set it and we're done... */
72     if (new_brk < brk_page) {
73         target_brk = new_brk;
74         return 0;
75     }
76 
77     /* We need to allocate more memory after the brk... */
78     new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
79     mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
80                                         PROT_READ|PROT_WRITE,
81                                         MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0));
82 
83     if (!is_error(mapped_addr))
84         target_brk = new_brk;
85     else
86         return mapped_addr;
87 
88     return 0;
89 }
90 
91 #ifdef __FreeBSD__
92 /*
93  * XXX this uses the undocumented oidfmt interface to find the kind of
94  * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt()
95  * (this is mostly copied from src/sbin/sysctl/sysctl.c)
96  */
97 static int
oidfmt(int * oid,int len,char * fmt,uint32_t * kind)98 oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
99 {
100     int qoid[CTL_MAXNAME+2];
101     uint8_t buf[BUFSIZ];
102     int i;
103     size_t j;
104 
105     qoid[0] = 0;
106     qoid[1] = 4;
107     memcpy(qoid + 2, oid, len * sizeof(int));
108 
109     j = sizeof(buf);
110     i = sysctl(qoid, len + 2, buf, &j, 0, 0);
111     if (i)
112         return i;
113 
114     if (kind)
115         *kind = *(uint32_t *)buf;
116 
117     if (fmt)
118         strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
119     return (0);
120 }
121 
122 /*
123  * try and convert sysctl return data for the target.
124  * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT.
125  */
sysctl_oldcvt(void * holdp,size_t holdlen,uint32_t kind)126 static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
127 {
128     switch (kind & CTLTYPE) {
129     case CTLTYPE_INT:
130     case CTLTYPE_UINT:
131         *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp);
132         break;
133 #ifdef TARGET_ABI32
134     case CTLTYPE_LONG:
135     case CTLTYPE_ULONG:
136         *(uint32_t *)holdp = tswap32(*(long *)holdp);
137         break;
138 #else
139     case CTLTYPE_LONG:
140         *(uint64_t *)holdp = tswap64(*(long *)holdp);
141         break;
142     case CTLTYPE_ULONG:
143         *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
144         break;
145 #endif
146 #ifdef CTLTYPE_U64
147     case CTLTYPE_S64:
148     case CTLTYPE_U64:
149 #else
150     case CTLTYPE_QUAD:
151 #endif
152         *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
153         break;
154     case CTLTYPE_STRING:
155         break;
156     default:
157         /* XXX unhandled */
158         return -1;
159     }
160     return 0;
161 }
162 
163 /* XXX this needs to be emulated on non-FreeBSD hosts... */
do_freebsd_sysctl(abi_ulong namep,int32_t namelen,abi_ulong oldp,abi_ulong oldlenp,abi_ulong newp,abi_ulong newlen)164 static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
165                           abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
166 {
167     abi_long ret;
168     void *hnamep, *holdp, *hnewp = NULL;
169     size_t holdlen;
170     abi_ulong oldlen = 0;
171     int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i;
172     uint32_t kind = 0;
173 
174     if (oldlenp)
175         get_user_ual(oldlen, oldlenp);
176     if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
177         return -TARGET_EFAULT;
178     if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
179         return -TARGET_EFAULT;
180     if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
181         return -TARGET_EFAULT;
182     holdlen = oldlen;
183     for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
184        *q++ = tswap32(*p);
185     oidfmt(snamep, namelen, NULL, &kind);
186     /* XXX swap hnewp */
187     ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
188     if (!ret)
189         sysctl_oldcvt(holdp, holdlen, kind);
190     put_user_ual(holdlen, oldlenp);
191     unlock_user(hnamep, namep, 0);
192     unlock_user(holdp, oldp, holdlen);
193     if (hnewp)
194         unlock_user(hnewp, newp, 0);
195     g_free(snamep);
196     return ret;
197 }
198 #endif
199 
200 /* FIXME
201  * lock_iovec()/unlock_iovec() have a return code of 0 for success where
202  * other lock functions have a return code of 0 for failure.
203  */
lock_iovec(int type,struct iovec * vec,abi_ulong target_addr,int count,int copy)204 static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
205                            int count, int copy)
206 {
207     struct target_iovec *target_vec;
208     abi_ulong base;
209     int i;
210 
211     target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
212     if (!target_vec)
213         return -TARGET_EFAULT;
214     for (i = 0;i < count; i++) {
215         base = tswapl(target_vec[i].iov_base);
216         vec[i].iov_len = tswapl(target_vec[i].iov_len);
217         if (vec[i].iov_len != 0) {
218             vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
219             /* Don't check lock_user return value. We must call writev even
220                if a element has invalid base address. */
221         } else {
222             /* zero length pointer is ignored */
223             vec[i].iov_base = NULL;
224         }
225     }
226     unlock_user (target_vec, target_addr, 0);
227     return 0;
228 }
229 
unlock_iovec(struct iovec * vec,abi_ulong target_addr,int count,int copy)230 static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
231                              int count, int copy)
232 {
233     struct target_iovec *target_vec;
234     abi_ulong base;
235     int i;
236 
237     target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
238     if (!target_vec)
239         return -TARGET_EFAULT;
240     for (i = 0;i < count; i++) {
241         if (target_vec[i].iov_base) {
242             base = tswapl(target_vec[i].iov_base);
243             unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
244         }
245     }
246     unlock_user (target_vec, target_addr, 0);
247 
248     return 0;
249 }
250 
251 /* do_syscall() should always have a single exit point at the end so
252    that actions, such as logging of syscall results, can be performed.
253    All errnos that do_syscall() returns must be -TARGET_<errcode>. */
do_freebsd_syscall(void * cpu_env,int num,abi_long arg1,abi_long arg2,abi_long arg3,abi_long arg4,abi_long arg5,abi_long arg6,abi_long arg7,abi_long arg8)254 abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
255                             abi_long arg2, abi_long arg3, abi_long arg4,
256                             abi_long arg5, abi_long arg6, abi_long arg7,
257                             abi_long arg8)
258 {
259     CPUState *cpu = env_cpu(cpu_env);
260     abi_long ret;
261     void *p;
262 
263 #ifdef DEBUG
264     gemu_log("freebsd syscall %d\n", num);
265 #endif
266     record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
267 
268     if (do_strace)
269         print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
270 
271     switch (num) {
272     case TARGET_FREEBSD_NR_exit:
273 #ifdef CONFIG_GPROF
274         _mcleanup();
275 #endif
276         gdb_exit(arg1);
277         qemu_plugin_user_exit();
278         /* XXX: should free thread stack and CPU env */
279         _exit(arg1);
280         ret = 0; /* avoid warning */
281         break;
282     case TARGET_FREEBSD_NR_read:
283         if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
284             goto efault;
285         ret = get_errno(read(arg1, p, arg3));
286         unlock_user(p, arg2, ret);
287         break;
288     case TARGET_FREEBSD_NR_write:
289         if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
290             goto efault;
291         ret = get_errno(write(arg1, p, arg3));
292         unlock_user(p, arg2, 0);
293         break;
294     case TARGET_FREEBSD_NR_writev:
295         {
296             int count = arg3;
297             struct iovec *vec;
298 
299             vec = alloca(count * sizeof(struct iovec));
300             if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
301                 goto efault;
302             ret = get_errno(writev(arg1, vec, count));
303             unlock_iovec(vec, arg2, count, 0);
304         }
305         break;
306     case TARGET_FREEBSD_NR_open:
307         if (!(p = lock_user_string(arg1)))
308             goto efault;
309         ret = get_errno(open(path(p),
310                              target_to_host_bitmask(arg2, fcntl_flags_tbl),
311                              arg3));
312         unlock_user(p, arg1, 0);
313         break;
314     case TARGET_FREEBSD_NR_mmap:
315         ret = get_errno(target_mmap(arg1, arg2, arg3,
316                                     target_to_host_bitmask(arg4, mmap_flags_tbl),
317                                     arg5,
318                                     arg6));
319         break;
320     case TARGET_FREEBSD_NR_mprotect:
321         ret = get_errno(target_mprotect(arg1, arg2, arg3));
322         break;
323     case TARGET_FREEBSD_NR_break:
324         ret = do_obreak(arg1);
325         break;
326 #ifdef __FreeBSD__
327     case TARGET_FREEBSD_NR___sysctl:
328         ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
329         break;
330 #endif
331     case TARGET_FREEBSD_NR_sysarch:
332         ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
333         break;
334     case TARGET_FREEBSD_NR_syscall:
335     case TARGET_FREEBSD_NR___syscall:
336         ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
337         break;
338     default:
339         ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
340         break;
341     }
342  fail:
343 #ifdef DEBUG
344     gemu_log(" = %ld\n", ret);
345 #endif
346     if (do_strace)
347         print_freebsd_syscall_ret(num, ret);
348 
349     record_syscall_return(cpu, num, ret);
350     return ret;
351  efault:
352     ret = -TARGET_EFAULT;
353     goto fail;
354 }
355 
do_netbsd_syscall(void * cpu_env,int num,abi_long arg1,abi_long arg2,abi_long arg3,abi_long arg4,abi_long arg5,abi_long arg6)356 abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
357                            abi_long arg2, abi_long arg3, abi_long arg4,
358                            abi_long arg5, abi_long arg6)
359 {
360     CPUState *cpu = env_cpu(cpu_env);
361     abi_long ret;
362     void *p;
363 
364 #ifdef DEBUG
365     gemu_log("netbsd syscall %d\n", num);
366 #endif
367 
368     record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
369 
370     if (do_strace)
371         print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
372 
373     switch (num) {
374     case TARGET_NETBSD_NR_exit:
375 #ifdef CONFIG_GPROF
376         _mcleanup();
377 #endif
378         gdb_exit(arg1);
379         qemu_plugin_user_exit();
380         /* XXX: should free thread stack and CPU env */
381         _exit(arg1);
382         ret = 0; /* avoid warning */
383         break;
384     case TARGET_NETBSD_NR_read:
385         if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
386             goto efault;
387         ret = get_errno(read(arg1, p, arg3));
388         unlock_user(p, arg2, ret);
389         break;
390     case TARGET_NETBSD_NR_write:
391         if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
392             goto efault;
393         ret = get_errno(write(arg1, p, arg3));
394         unlock_user(p, arg2, 0);
395         break;
396     case TARGET_NETBSD_NR_open:
397         if (!(p = lock_user_string(arg1)))
398             goto efault;
399         ret = get_errno(open(path(p),
400                              target_to_host_bitmask(arg2, fcntl_flags_tbl),
401                              arg3));
402         unlock_user(p, arg1, 0);
403         break;
404     case TARGET_NETBSD_NR_mmap:
405         ret = get_errno(target_mmap(arg1, arg2, arg3,
406                                     target_to_host_bitmask(arg4, mmap_flags_tbl),
407                                     arg5,
408                                     arg6));
409         break;
410     case TARGET_NETBSD_NR_mprotect:
411         ret = get_errno(target_mprotect(arg1, arg2, arg3));
412         break;
413     case TARGET_NETBSD_NR_syscall:
414     case TARGET_NETBSD_NR___syscall:
415         ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
416         break;
417     default:
418         ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
419         break;
420     }
421  fail:
422 #ifdef DEBUG
423     gemu_log(" = %ld\n", ret);
424 #endif
425     if (do_strace)
426         print_netbsd_syscall_ret(num, ret);
427 
428     record_syscall_return(cpu, num, ret);
429     return ret;
430  efault:
431     ret = -TARGET_EFAULT;
432     goto fail;
433 }
434 
do_openbsd_syscall(void * cpu_env,int num,abi_long arg1,abi_long arg2,abi_long arg3,abi_long arg4,abi_long arg5,abi_long arg6)435 abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
436                             abi_long arg2, abi_long arg3, abi_long arg4,
437                             abi_long arg5, abi_long arg6)
438 {
439     CPUState *cpu = env_cpu(cpu_env);
440     abi_long ret;
441     void *p;
442 
443 #ifdef DEBUG
444     gemu_log("openbsd syscall %d\n", num);
445 #endif
446 
447     record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
448 
449     if (do_strace)
450         print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
451 
452     switch (num) {
453     case TARGET_OPENBSD_NR_exit:
454 #ifdef CONFIG_GPROF
455         _mcleanup();
456 #endif
457         gdb_exit(arg1);
458         qemu_plugin_user_exit();
459         /* XXX: should free thread stack and CPU env */
460         _exit(arg1);
461         ret = 0; /* avoid warning */
462         break;
463     case TARGET_OPENBSD_NR_read:
464         if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
465             goto efault;
466         ret = get_errno(read(arg1, p, arg3));
467         unlock_user(p, arg2, ret);
468         break;
469     case TARGET_OPENBSD_NR_write:
470         if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
471             goto efault;
472         ret = get_errno(write(arg1, p, arg3));
473         unlock_user(p, arg2, 0);
474         break;
475     case TARGET_OPENBSD_NR_open:
476         if (!(p = lock_user_string(arg1)))
477             goto efault;
478         ret = get_errno(open(path(p),
479                              target_to_host_bitmask(arg2, fcntl_flags_tbl),
480                              arg3));
481         unlock_user(p, arg1, 0);
482         break;
483     case TARGET_OPENBSD_NR_mmap:
484         ret = get_errno(target_mmap(arg1, arg2, arg3,
485                                     target_to_host_bitmask(arg4, mmap_flags_tbl),
486                                     arg5,
487                                     arg6));
488         break;
489     case TARGET_OPENBSD_NR_mprotect:
490         ret = get_errno(target_mprotect(arg1, arg2, arg3));
491         break;
492     case TARGET_OPENBSD_NR_syscall:
493     case TARGET_OPENBSD_NR___syscall:
494         ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
495         break;
496     default:
497         ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
498         break;
499     }
500  fail:
501 #ifdef DEBUG
502     gemu_log(" = %ld\n", ret);
503 #endif
504     if (do_strace)
505         print_openbsd_syscall_ret(num, ret);
506 
507     record_syscall_return(cpu, num, ret);
508     return ret;
509  efault:
510     ret = -TARGET_EFAULT;
511     goto fail;
512 }
513 
syscall_init(void)514 void syscall_init(void)
515 {
516 }
517