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