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 static inline 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     else
42         return ret;
43 }
44 
45 #define target_to_host_bitmask(x, tbl) (x)
46 
is_error(abi_long ret)47 static inline int 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 #if defined(TARGET_I386)
do_freebsd_sysarch(CPUX86State * env,int op,abi_ulong parms)92 static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms)
93 {
94     abi_long ret = 0;
95     abi_ulong val;
96     int idx;
97 
98     switch(op) {
99 #ifdef TARGET_ABI32
100     case TARGET_FREEBSD_I386_SET_GSBASE:
101     case TARGET_FREEBSD_I386_SET_FSBASE:
102         if (op == TARGET_FREEBSD_I386_SET_GSBASE)
103 #else
104     case TARGET_FREEBSD_AMD64_SET_GSBASE:
105     case TARGET_FREEBSD_AMD64_SET_FSBASE:
106         if (op == TARGET_FREEBSD_AMD64_SET_GSBASE)
107 #endif
108             idx = R_GS;
109         else
110             idx = R_FS;
111         if (get_user(val, parms, abi_ulong))
112             return -TARGET_EFAULT;
113         cpu_x86_load_seg(env, idx, 0);
114         env->segs[idx].base = val;
115         break;
116 #ifdef TARGET_ABI32
117     case TARGET_FREEBSD_I386_GET_GSBASE:
118     case TARGET_FREEBSD_I386_GET_FSBASE:
119         if (op == TARGET_FREEBSD_I386_GET_GSBASE)
120 #else
121     case TARGET_FREEBSD_AMD64_GET_GSBASE:
122     case TARGET_FREEBSD_AMD64_GET_FSBASE:
123         if (op == TARGET_FREEBSD_AMD64_GET_GSBASE)
124 #endif
125             idx = R_GS;
126         else
127             idx = R_FS;
128         val = env->segs[idx].base;
129         if (put_user(val, parms, abi_ulong))
130             return -TARGET_EFAULT;
131         break;
132     /* XXX handle the others... */
133     default:
134         ret = -TARGET_EINVAL;
135         break;
136     }
137     return ret;
138 }
139 #endif
140 
141 #ifdef TARGET_SPARC
do_freebsd_sysarch(void * env,int op,abi_ulong parms)142 static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms)
143 {
144     /* XXX handle
145      * TARGET_FREEBSD_SPARC_UTRAP_INSTALL,
146      * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL
147      */
148     return -TARGET_EINVAL;
149 }
150 #endif
151 
152 #ifdef __FreeBSD__
153 /*
154  * XXX this uses the undocumented oidfmt interface to find the kind of
155  * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt()
156  * (this is mostly copied from src/sbin/sysctl/sysctl.c)
157  */
158 static int
oidfmt(int * oid,int len,char * fmt,uint32_t * kind)159 oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
160 {
161     int qoid[CTL_MAXNAME+2];
162     uint8_t buf[BUFSIZ];
163     int i;
164     size_t j;
165 
166     qoid[0] = 0;
167     qoid[1] = 4;
168     memcpy(qoid + 2, oid, len * sizeof(int));
169 
170     j = sizeof(buf);
171     i = sysctl(qoid, len + 2, buf, &j, 0, 0);
172     if (i)
173         return i;
174 
175     if (kind)
176         *kind = *(uint32_t *)buf;
177 
178     if (fmt)
179         strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
180     return (0);
181 }
182 
183 /*
184  * try and convert sysctl return data for the target.
185  * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT.
186  */
sysctl_oldcvt(void * holdp,size_t holdlen,uint32_t kind)187 static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
188 {
189     switch (kind & CTLTYPE) {
190     case CTLTYPE_INT:
191     case CTLTYPE_UINT:
192         *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp);
193         break;
194 #ifdef TARGET_ABI32
195     case CTLTYPE_LONG:
196     case CTLTYPE_ULONG:
197         *(uint32_t *)holdp = tswap32(*(long *)holdp);
198         break;
199 #else
200     case CTLTYPE_LONG:
201         *(uint64_t *)holdp = tswap64(*(long *)holdp);
202     case CTLTYPE_ULONG:
203         *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
204         break;
205 #endif
206 #ifdef CTLTYPE_U64
207     case CTLTYPE_S64:
208     case CTLTYPE_U64:
209 #else
210     case CTLTYPE_QUAD:
211 #endif
212         *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
213         break;
214     case CTLTYPE_STRING:
215         break;
216     default:
217         /* XXX unhandled */
218         return -1;
219     }
220     return 0;
221 }
222 
223 /* 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)224 static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
225                           abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
226 {
227     abi_long ret;
228     void *hnamep, *holdp, *hnewp = NULL;
229     size_t holdlen;
230     abi_ulong oldlen = 0;
231     int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i;
232     uint32_t kind = 0;
233 
234     if (oldlenp)
235         get_user_ual(oldlen, oldlenp);
236     if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
237         return -TARGET_EFAULT;
238     if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
239         return -TARGET_EFAULT;
240     if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
241         return -TARGET_EFAULT;
242     holdlen = oldlen;
243     for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
244        *q++ = tswap32(*p);
245     oidfmt(snamep, namelen, NULL, &kind);
246     /* XXX swap hnewp */
247     ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
248     if (!ret)
249         sysctl_oldcvt(holdp, holdlen, kind);
250     put_user_ual(holdlen, oldlenp);
251     unlock_user(hnamep, namep, 0);
252     unlock_user(holdp, oldp, holdlen);
253     if (hnewp)
254         unlock_user(hnewp, newp, 0);
255     g_free(snamep);
256     return ret;
257 }
258 #endif
259 
260 /* FIXME
261  * lock_iovec()/unlock_iovec() have a return code of 0 for success where
262  * other lock functions have a return code of 0 for failure.
263  */
lock_iovec(int type,struct iovec * vec,abi_ulong target_addr,int count,int copy)264 static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
265                            int count, int copy)
266 {
267     struct target_iovec *target_vec;
268     abi_ulong base;
269     int i;
270 
271     target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
272     if (!target_vec)
273         return -TARGET_EFAULT;
274     for(i = 0;i < count; i++) {
275         base = tswapl(target_vec[i].iov_base);
276         vec[i].iov_len = tswapl(target_vec[i].iov_len);
277         if (vec[i].iov_len != 0) {
278             vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
279             /* Don't check lock_user return value. We must call writev even
280                if a element has invalid base address. */
281         } else {
282             /* zero length pointer is ignored */
283             vec[i].iov_base = NULL;
284         }
285     }
286     unlock_user (target_vec, target_addr, 0);
287     return 0;
288 }
289 
unlock_iovec(struct iovec * vec,abi_ulong target_addr,int count,int copy)290 static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
291                              int count, int copy)
292 {
293     struct target_iovec *target_vec;
294     abi_ulong base;
295     int i;
296 
297     target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
298     if (!target_vec)
299         return -TARGET_EFAULT;
300     for(i = 0;i < count; i++) {
301         if (target_vec[i].iov_base) {
302             base = tswapl(target_vec[i].iov_base);
303             unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
304         }
305     }
306     unlock_user (target_vec, target_addr, 0);
307 
308     return 0;
309 }
310 
311 /* do_syscall() should always have a single exit point at the end so
312    that actions, such as logging of syscall results, can be performed.
313    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)314 abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
315                             abi_long arg2, abi_long arg3, abi_long arg4,
316                             abi_long arg5, abi_long arg6, abi_long arg7,
317                             abi_long arg8)
318 {
319     CPUState *cpu = env_cpu(cpu_env);
320     abi_long ret;
321     void *p;
322 
323 #ifdef DEBUG
324     gemu_log("freebsd syscall %d\n", num);
325 #endif
326     record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
327 
328     if(do_strace)
329         print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
330 
331     switch(num) {
332     case TARGET_FREEBSD_NR_exit:
333 #ifdef CONFIG_GPROF
334         _mcleanup();
335 #endif
336         gdb_exit(cpu_env, arg1);
337         qemu_plugin_atexit_cb();
338         /* XXX: should free thread stack and CPU env */
339         _exit(arg1);
340         ret = 0; /* avoid warning */
341         break;
342     case TARGET_FREEBSD_NR_read:
343         if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
344             goto efault;
345         ret = get_errno(read(arg1, p, arg3));
346         unlock_user(p, arg2, ret);
347         break;
348     case TARGET_FREEBSD_NR_write:
349         if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
350             goto efault;
351         ret = get_errno(write(arg1, p, arg3));
352         unlock_user(p, arg2, 0);
353         break;
354     case TARGET_FREEBSD_NR_writev:
355         {
356             int count = arg3;
357             struct iovec *vec;
358 
359             vec = alloca(count * sizeof(struct iovec));
360             if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
361                 goto efault;
362             ret = get_errno(writev(arg1, vec, count));
363             unlock_iovec(vec, arg2, count, 0);
364         }
365         break;
366     case TARGET_FREEBSD_NR_open:
367         if (!(p = lock_user_string(arg1)))
368             goto efault;
369         ret = get_errno(open(path(p),
370                              target_to_host_bitmask(arg2, fcntl_flags_tbl),
371                              arg3));
372         unlock_user(p, arg1, 0);
373         break;
374     case TARGET_FREEBSD_NR_mmap:
375         ret = get_errno(target_mmap(arg1, arg2, arg3,
376                                     target_to_host_bitmask(arg4, mmap_flags_tbl),
377                                     arg5,
378                                     arg6));
379         break;
380     case TARGET_FREEBSD_NR_mprotect:
381         ret = get_errno(target_mprotect(arg1, arg2, arg3));
382         break;
383     case TARGET_FREEBSD_NR_break:
384         ret = do_obreak(arg1);
385         break;
386 #ifdef __FreeBSD__
387     case TARGET_FREEBSD_NR___sysctl:
388         ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
389         break;
390 #endif
391     case TARGET_FREEBSD_NR_sysarch:
392         ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
393         break;
394     case TARGET_FREEBSD_NR_syscall:
395     case TARGET_FREEBSD_NR___syscall:
396         ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
397         break;
398     default:
399         ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
400         break;
401     }
402  fail:
403 #ifdef DEBUG
404     gemu_log(" = %ld\n", ret);
405 #endif
406     if (do_strace)
407         print_freebsd_syscall_ret(num, ret);
408 
409     record_syscall_return(cpu, num, ret);
410     return ret;
411  efault:
412     ret = -TARGET_EFAULT;
413     goto fail;
414 }
415 
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)416 abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
417                            abi_long arg2, abi_long arg3, abi_long arg4,
418                            abi_long arg5, abi_long arg6)
419 {
420     CPUState *cpu = env_cpu(cpu_env);
421     abi_long ret;
422     void *p;
423 
424 #ifdef DEBUG
425     gemu_log("netbsd syscall %d\n", num);
426 #endif
427 
428     record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
429 
430     if(do_strace)
431         print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
432 
433     switch(num) {
434     case TARGET_NETBSD_NR_exit:
435 #ifdef CONFIG_GPROF
436         _mcleanup();
437 #endif
438         gdb_exit(cpu_env, arg1);
439         qemu_plugin_atexit_cb();
440         /* XXX: should free thread stack and CPU env */
441         _exit(arg1);
442         ret = 0; /* avoid warning */
443         break;
444     case TARGET_NETBSD_NR_read:
445         if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
446             goto efault;
447         ret = get_errno(read(arg1, p, arg3));
448         unlock_user(p, arg2, ret);
449         break;
450     case TARGET_NETBSD_NR_write:
451         if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
452             goto efault;
453         ret = get_errno(write(arg1, p, arg3));
454         unlock_user(p, arg2, 0);
455         break;
456     case TARGET_NETBSD_NR_open:
457         if (!(p = lock_user_string(arg1)))
458             goto efault;
459         ret = get_errno(open(path(p),
460                              target_to_host_bitmask(arg2, fcntl_flags_tbl),
461                              arg3));
462         unlock_user(p, arg1, 0);
463         break;
464     case TARGET_NETBSD_NR_mmap:
465         ret = get_errno(target_mmap(arg1, arg2, arg3,
466                                     target_to_host_bitmask(arg4, mmap_flags_tbl),
467                                     arg5,
468                                     arg6));
469         break;
470     case TARGET_NETBSD_NR_mprotect:
471         ret = get_errno(target_mprotect(arg1, arg2, arg3));
472         break;
473     case TARGET_NETBSD_NR_syscall:
474     case TARGET_NETBSD_NR___syscall:
475         ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
476         break;
477     default:
478         ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
479         break;
480     }
481  fail:
482 #ifdef DEBUG
483     gemu_log(" = %ld\n", ret);
484 #endif
485     if (do_strace)
486         print_netbsd_syscall_ret(num, ret);
487 
488     record_syscall_return(cpu, num, ret);
489     return ret;
490  efault:
491     ret = -TARGET_EFAULT;
492     goto fail;
493 }
494 
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)495 abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
496                             abi_long arg2, abi_long arg3, abi_long arg4,
497                             abi_long arg5, abi_long arg6)
498 {
499     CPUState *cpu = env_cpu(cpu_env);
500     abi_long ret;
501     void *p;
502 
503 #ifdef DEBUG
504     gemu_log("openbsd syscall %d\n", num);
505 #endif
506 
507     record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
508 
509     if(do_strace)
510         print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
511 
512     switch(num) {
513     case TARGET_OPENBSD_NR_exit:
514 #ifdef CONFIG_GPROF
515         _mcleanup();
516 #endif
517         gdb_exit(cpu_env, arg1);
518         qemu_plugin_atexit_cb();
519         /* XXX: should free thread stack and CPU env */
520         _exit(arg1);
521         ret = 0; /* avoid warning */
522         break;
523     case TARGET_OPENBSD_NR_read:
524         if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
525             goto efault;
526         ret = get_errno(read(arg1, p, arg3));
527         unlock_user(p, arg2, ret);
528         break;
529     case TARGET_OPENBSD_NR_write:
530         if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
531             goto efault;
532         ret = get_errno(write(arg1, p, arg3));
533         unlock_user(p, arg2, 0);
534         break;
535     case TARGET_OPENBSD_NR_open:
536         if (!(p = lock_user_string(arg1)))
537             goto efault;
538         ret = get_errno(open(path(p),
539                              target_to_host_bitmask(arg2, fcntl_flags_tbl),
540                              arg3));
541         unlock_user(p, arg1, 0);
542         break;
543     case TARGET_OPENBSD_NR_mmap:
544         ret = get_errno(target_mmap(arg1, arg2, arg3,
545                                     target_to_host_bitmask(arg4, mmap_flags_tbl),
546                                     arg5,
547                                     arg6));
548         break;
549     case TARGET_OPENBSD_NR_mprotect:
550         ret = get_errno(target_mprotect(arg1, arg2, arg3));
551         break;
552     case TARGET_OPENBSD_NR_syscall:
553     case TARGET_OPENBSD_NR___syscall:
554         ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
555         break;
556     default:
557         ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
558         break;
559     }
560  fail:
561 #ifdef DEBUG
562     gemu_log(" = %ld\n", ret);
563 #endif
564     if (do_strace)
565         print_openbsd_syscall_ret(num, ret);
566 
567     record_syscall_return(cpu, num, ret);
568     return ret;
569  efault:
570     ret = -TARGET_EFAULT;
571     goto fail;
572 }
573 
syscall_init(void)574 void syscall_init(void)
575 {
576 }
577