Commit 8853f86e1da70e79464aa3329f7b84a27a199bad
1 parent
e374bfa3
shm support, more setsockopt and getsockopt calls, fds fix (initial patch by Paul McKerras)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@638 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
206 additions
and
52 deletions
linux-user/syscall.c
| ... | ... | @@ -41,8 +41,10 @@ |
| 41 | 41 | #include <sys/uio.h> |
| 42 | 42 | #include <sys/poll.h> |
| 43 | 43 | #include <sys/times.h> |
| 44 | +#include <sys/shm.h> | |
| 44 | 45 | #include <utime.h> |
| 45 | 46 | //#include <sys/user.h> |
| 47 | +#include <netinet/ip.h> | |
| 46 | 48 | #include <netinet/tcp.h> |
| 47 | 49 | |
| 48 | 50 | #define termios host_termios |
| ... | ... | @@ -325,7 +327,7 @@ static inline void host_to_target_fds(target_long *target_fds, |
| 325 | 327 | target_long v; |
| 326 | 328 | |
| 327 | 329 | if (target_fds) { |
| 328 | - nw = n / TARGET_LONG_BITS; | |
| 330 | + nw = (n + TARGET_LONG_BITS - 1) / TARGET_LONG_BITS; | |
| 329 | 331 | k = 0; |
| 330 | 332 | for(i = 0;i < nw; i++) { |
| 331 | 333 | v = 0; |
| ... | ... | @@ -530,61 +532,116 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, |
| 530 | 532 | static long do_setsockopt(int sockfd, int level, int optname, |
| 531 | 533 | void *optval, socklen_t optlen) |
| 532 | 534 | { |
| 533 | - if (level == SOL_TCP) { | |
| 535 | + int val, ret; | |
| 536 | + | |
| 537 | + switch(level) { | |
| 538 | + case SOL_TCP: | |
| 534 | 539 | /* TCP options all take an 'int' value. */ |
| 535 | - int val; | |
| 536 | - | |
| 537 | 540 | if (optlen < sizeof(uint32_t)) |
| 538 | 541 | return -EINVAL; |
| 539 | - | |
| 540 | - val = tswap32(*(uint32_t *)optval); | |
| 541 | - return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); | |
| 542 | - } | |
| 543 | - | |
| 544 | - else if (level != SOL_SOCKET) { | |
| 545 | - gemu_log("Unsupported setsockopt level: %d\n", level); | |
| 546 | - return -ENOSYS; | |
| 547 | - } | |
| 548 | - | |
| 549 | - switch (optname) { | |
| 550 | - /* Options with 'int' argument. */ | |
| 551 | - case SO_DEBUG: | |
| 552 | - case SO_REUSEADDR: | |
| 553 | - case SO_TYPE: | |
| 554 | - case SO_ERROR: | |
| 555 | - case SO_DONTROUTE: | |
| 556 | - case SO_BROADCAST: | |
| 557 | - case SO_SNDBUF: | |
| 558 | - case SO_RCVBUF: | |
| 559 | - case SO_KEEPALIVE: | |
| 560 | - case SO_OOBINLINE: | |
| 561 | - case SO_NO_CHECK: | |
| 562 | - case SO_PRIORITY: | |
| 563 | - case SO_BSDCOMPAT: | |
| 564 | - case SO_PASSCRED: | |
| 565 | - case SO_TIMESTAMP: | |
| 566 | - case SO_RCVLOWAT: | |
| 567 | - case SO_RCVTIMEO: | |
| 568 | - case SO_SNDTIMEO: | |
| 569 | - { | |
| 570 | - int val; | |
| 571 | - if (optlen < sizeof(uint32_t)) | |
| 572 | - return -EINVAL; | |
| 573 | - val = tswap32(*(uint32_t *)optval); | |
| 574 | - return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); | |
| 575 | - } | |
| 576 | - | |
| 542 | + | |
| 543 | + if (get_user(val, (uint32_t *)optval)) | |
| 544 | + return -EFAULT; | |
| 545 | + ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); | |
| 546 | + break; | |
| 547 | + case SOL_IP: | |
| 548 | + switch(optname) { | |
| 549 | + case IP_HDRINCL: | |
| 550 | + val = 0; | |
| 551 | + if (optlen >= sizeof(uint32_t)) { | |
| 552 | + if (get_user(val, (uint32_t *)optval)) | |
| 553 | + return -EFAULT; | |
| 554 | + } else if (optlen >= 1) { | |
| 555 | + if (get_user(val, (uint8_t *)optval)) | |
| 556 | + return -EFAULT; | |
| 557 | + } | |
| 558 | + ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); | |
| 559 | + break; | |
| 560 | + default: | |
| 561 | + goto unimplemented; | |
| 562 | + } | |
| 563 | + break; | |
| 564 | + case SOL_SOCKET: | |
| 565 | + switch (optname) { | |
| 566 | + /* Options with 'int' argument. */ | |
| 567 | + case SO_DEBUG: | |
| 568 | + case SO_REUSEADDR: | |
| 569 | + case SO_TYPE: | |
| 570 | + case SO_ERROR: | |
| 571 | + case SO_DONTROUTE: | |
| 572 | + case SO_BROADCAST: | |
| 573 | + case SO_SNDBUF: | |
| 574 | + case SO_RCVBUF: | |
| 575 | + case SO_KEEPALIVE: | |
| 576 | + case SO_OOBINLINE: | |
| 577 | + case SO_NO_CHECK: | |
| 578 | + case SO_PRIORITY: | |
| 579 | + case SO_BSDCOMPAT: | |
| 580 | + case SO_PASSCRED: | |
| 581 | + case SO_TIMESTAMP: | |
| 582 | + case SO_RCVLOWAT: | |
| 583 | + case SO_RCVTIMEO: | |
| 584 | + case SO_SNDTIMEO: | |
| 585 | + if (optlen < sizeof(uint32_t)) | |
| 586 | + return -EINVAL; | |
| 587 | + if (get_user(val, (uint32_t *)optval)) | |
| 588 | + return -EFAULT; | |
| 589 | + ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val))); | |
| 590 | + break; | |
| 591 | + default: | |
| 592 | + goto unimplemented; | |
| 593 | + } | |
| 594 | + break; | |
| 577 | 595 | default: |
| 578 | - gemu_log("Unsupported setsockopt SOL_SOCKET option: %d\n", optname); | |
| 579 | - return -ENOSYS; | |
| 596 | + unimplemented: | |
| 597 | + gemu_log("Unsupported setsockopt level=%d optname=%d \n", level, optname); | |
| 598 | + ret = -ENOSYS; | |
| 580 | 599 | } |
| 600 | + return ret; | |
| 581 | 601 | } |
| 582 | 602 | |
| 583 | 603 | static long do_getsockopt(int sockfd, int level, int optname, |
| 584 | 604 | void *optval, socklen_t *optlen) |
| 585 | 605 | { |
| 586 | - gemu_log("getsockopt not yet supported\n"); | |
| 587 | - return -ENOSYS; | |
| 606 | + int len, lv, val, ret; | |
| 607 | + | |
| 608 | + switch(level) { | |
| 609 | + case SOL_SOCKET: | |
| 610 | + switch (optname) { | |
| 611 | + case SO_LINGER: | |
| 612 | + case SO_RCVTIMEO: | |
| 613 | + case SO_SNDTIMEO: | |
| 614 | + case SO_PEERCRED: | |
| 615 | + case SO_PEERNAME: | |
| 616 | + /* These don't just return a single integer */ | |
| 617 | + goto unimplemented; | |
| 618 | + default: | |
| 619 | + if (get_user(len, optlen)) | |
| 620 | + return -EFAULT; | |
| 621 | + if (len < 0) | |
| 622 | + return -EINVAL; | |
| 623 | + lv = sizeof(int); | |
| 624 | + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); | |
| 625 | + if (ret < 0) | |
| 626 | + return ret; | |
| 627 | + val = tswap32(val); | |
| 628 | + if (len > lv) | |
| 629 | + len = lv; | |
| 630 | + if (copy_to_user(optval, &val, len)) | |
| 631 | + return -EFAULT; | |
| 632 | + if (put_user(len, optlen)) | |
| 633 | + return -EFAULT; | |
| 634 | + break; | |
| 635 | + } | |
| 636 | + break; | |
| 637 | + default: | |
| 638 | + unimplemented: | |
| 639 | + gemu_log("getsockopt level=%d optname=%d not yet supported\n", | |
| 640 | + level, optname); | |
| 641 | + ret = -ENOSYS; | |
| 642 | + break; | |
| 643 | + } | |
| 644 | + return ret; | |
| 588 | 645 | } |
| 589 | 646 | |
| 590 | 647 | static long do_socketcall(int num, int32_t *vptr) |
| ... | ... | @@ -807,12 +864,9 @@ static long do_socketcall(int num, int32_t *vptr) |
| 807 | 864 | int level = tswap32(vptr[1]); |
| 808 | 865 | int optname = tswap32(vptr[2]); |
| 809 | 866 | void *optval = (void *)tswap32(vptr[3]); |
| 810 | - uint32_t *target_len = (void *)tswap32(vptr[4]); | |
| 811 | - socklen_t optlen = tswap32(*target_len); | |
| 867 | + uint32_t *poptlen = (void *)tswap32(vptr[4]); | |
| 812 | 868 | |
| 813 | - ret = do_getsockopt(sockfd, level, optname, optval, &optlen); | |
| 814 | - if (!is_error(ret)) | |
| 815 | - *target_len = tswap32(optlen); | |
| 869 | + ret = do_getsockopt(sockfd, level, optname, optval, poptlen); | |
| 816 | 870 | } |
| 817 | 871 | break; |
| 818 | 872 | default: |
| ... | ... | @@ -823,6 +877,92 @@ static long do_socketcall(int num, int32_t *vptr) |
| 823 | 877 | return ret; |
| 824 | 878 | } |
| 825 | 879 | |
| 880 | + | |
| 881 | +#define N_SHM_REGIONS 32 | |
| 882 | + | |
| 883 | +static struct shm_region { | |
| 884 | + uint32_t start; | |
| 885 | + uint32_t size; | |
| 886 | +} shm_regions[N_SHM_REGIONS]; | |
| 887 | + | |
| 888 | +static long do_ipc(long call, long first, long second, long third, | |
| 889 | + long ptr, long fifth) | |
| 890 | +{ | |
| 891 | + int version; | |
| 892 | + long ret = 0; | |
| 893 | + unsigned long raddr; | |
| 894 | + struct shmid_ds shm_info; | |
| 895 | + int i; | |
| 896 | + | |
| 897 | + version = call >> 16; | |
| 898 | + call &= 0xffff; | |
| 899 | + | |
| 900 | + switch (call) { | |
| 901 | + case IPCOP_shmat: | |
| 902 | + /* SHM_* flags are the same on all linux platforms */ | |
| 903 | + ret = get_errno((long) shmat(first, (void *) ptr, second)); | |
| 904 | + if (is_error(ret)) | |
| 905 | + break; | |
| 906 | + raddr = ret; | |
| 907 | + /* find out the length of the shared memory segment */ | |
| 908 | + | |
| 909 | + ret = get_errno(shmctl(first, IPC_STAT, &shm_info)); | |
| 910 | + if (is_error(ret)) { | |
| 911 | + /* can't get length, bail out */ | |
| 912 | + shmdt((void *) raddr); | |
| 913 | + break; | |
| 914 | + } | |
| 915 | + page_set_flags(raddr, raddr + shm_info.shm_segsz, | |
| 916 | + PAGE_VALID | PAGE_READ | | |
| 917 | + ((second & SHM_RDONLY)? 0: PAGE_WRITE)); | |
| 918 | + for (i = 0; i < N_SHM_REGIONS; ++i) { | |
| 919 | + if (shm_regions[i].start == 0) { | |
| 920 | + shm_regions[i].start = raddr; | |
| 921 | + shm_regions[i].size = shm_info.shm_segsz; | |
| 922 | + break; | |
| 923 | + } | |
| 924 | + } | |
| 925 | + if (put_user(raddr, (uint32_t *)third)) | |
| 926 | + return -EFAULT; | |
| 927 | + ret = 0; | |
| 928 | + break; | |
| 929 | + case IPCOP_shmdt: | |
| 930 | + for (i = 0; i < N_SHM_REGIONS; ++i) { | |
| 931 | + if (shm_regions[i].start == ptr) { | |
| 932 | + shm_regions[i].start = 0; | |
| 933 | + page_set_flags(ptr, shm_regions[i].size, 0); | |
| 934 | + break; | |
| 935 | + } | |
| 936 | + } | |
| 937 | + ret = get_errno(shmdt((void *) ptr)); | |
| 938 | + break; | |
| 939 | + | |
| 940 | + case IPCOP_shmget: | |
| 941 | + /* IPC_* flag values are the same on all linux platforms */ | |
| 942 | + ret = get_errno(shmget(first, second, third)); | |
| 943 | + break; | |
| 944 | + | |
| 945 | + /* IPC_* and SHM_* command values are the same on all linux platforms */ | |
| 946 | + case IPCOP_shmctl: | |
| 947 | + switch(second) { | |
| 948 | + case IPC_RMID: | |
| 949 | + case SHM_LOCK: | |
| 950 | + case SHM_UNLOCK: | |
| 951 | + ret = get_errno(shmctl(first, second, NULL)); | |
| 952 | + break; | |
| 953 | + default: | |
| 954 | + goto unimplemented; | |
| 955 | + } | |
| 956 | + break; | |
| 957 | + default: | |
| 958 | + unimplemented: | |
| 959 | + gemu_log("Unsupported ipc call: %ld (version %d)\n", call, version); | |
| 960 | + ret = -ENOSYS; | |
| 961 | + break; | |
| 962 | + } | |
| 963 | + return ret; | |
| 964 | +} | |
| 965 | + | |
| 826 | 966 | /* kernel structure types definitions */ |
| 827 | 967 | #define IFNAMSIZ 16 |
| 828 | 968 | |
| ... | ... | @@ -2205,7 +2345,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, |
| 2205 | 2345 | case TARGET_NR_sysinfo: |
| 2206 | 2346 | goto unimplemented; |
| 2207 | 2347 | case TARGET_NR_ipc: |
| 2208 | - goto unimplemented; | |
| 2348 | + ret = do_ipc(arg1, arg2, arg3, arg4, arg5, arg6); | |
| 2349 | + break; | |
| 2209 | 2350 | case TARGET_NR_fsync: |
| 2210 | 2351 | ret = get_errno(fsync(arg1)); |
| 2211 | 2352 | break; | ... | ... |
linux-user/syscall_defs.h
| ... | ... | @@ -24,6 +24,19 @@ |
| 24 | 24 | #define SOCKOP_sendmsg 16 |
| 25 | 25 | #define SOCKOP_recvmsg 17 |
| 26 | 26 | |
| 27 | +#define IPCOP_semop 1 | |
| 28 | +#define IPCOP_semget 2 | |
| 29 | +#define IPCOP_semctl 3 | |
| 30 | +#define IPCOP_semtimedop 4 | |
| 31 | +#define IPCOP_msgsnd 11 | |
| 32 | +#define IPCOP_msgrcv 12 | |
| 33 | +#define IPCOP_msgget 13 | |
| 34 | +#define IPCOP_msgctl 14 | |
| 35 | +#define IPCOP_shmat 21 | |
| 36 | +#define IPCOP_shmdt 22 | |
| 37 | +#define IPCOP_shmget 23 | |
| 38 | +#define IPCOP_shmctl 24 | |
| 39 | + | |
| 27 | 40 | /* |
| 28 | 41 | * The following is for compatibility across the various Linux |
| 29 | 42 | * platforms. The i386 ioctl numbering scheme doesn't really enforce | ... | ... |