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,8 +41,10 @@ | ||
| 41 | #include <sys/uio.h> | 41 | #include <sys/uio.h> |
| 42 | #include <sys/poll.h> | 42 | #include <sys/poll.h> |
| 43 | #include <sys/times.h> | 43 | #include <sys/times.h> |
| 44 | +#include <sys/shm.h> | ||
| 44 | #include <utime.h> | 45 | #include <utime.h> |
| 45 | //#include <sys/user.h> | 46 | //#include <sys/user.h> |
| 47 | +#include <netinet/ip.h> | ||
| 46 | #include <netinet/tcp.h> | 48 | #include <netinet/tcp.h> |
| 47 | 49 | ||
| 48 | #define termios host_termios | 50 | #define termios host_termios |
| @@ -325,7 +327,7 @@ static inline void host_to_target_fds(target_long *target_fds, | @@ -325,7 +327,7 @@ static inline void host_to_target_fds(target_long *target_fds, | ||
| 325 | target_long v; | 327 | target_long v; |
| 326 | 328 | ||
| 327 | if (target_fds) { | 329 | if (target_fds) { |
| 328 | - nw = n / TARGET_LONG_BITS; | 330 | + nw = (n + TARGET_LONG_BITS - 1) / TARGET_LONG_BITS; |
| 329 | k = 0; | 331 | k = 0; |
| 330 | for(i = 0;i < nw; i++) { | 332 | for(i = 0;i < nw; i++) { |
| 331 | v = 0; | 333 | v = 0; |
| @@ -530,61 +532,116 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, | @@ -530,61 +532,116 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh, | ||
| 530 | static long do_setsockopt(int sockfd, int level, int optname, | 532 | static long do_setsockopt(int sockfd, int level, int optname, |
| 531 | void *optval, socklen_t optlen) | 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 | /* TCP options all take an 'int' value. */ | 539 | /* TCP options all take an 'int' value. */ |
| 535 | - int val; | ||
| 536 | - | ||
| 537 | if (optlen < sizeof(uint32_t)) | 540 | if (optlen < sizeof(uint32_t)) |
| 538 | return -EINVAL; | 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 | default: | 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 | static long do_getsockopt(int sockfd, int level, int optname, | 603 | static long do_getsockopt(int sockfd, int level, int optname, |
| 584 | void *optval, socklen_t *optlen) | 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 | static long do_socketcall(int num, int32_t *vptr) | 647 | static long do_socketcall(int num, int32_t *vptr) |
| @@ -807,12 +864,9 @@ static long do_socketcall(int num, int32_t *vptr) | @@ -807,12 +864,9 @@ static long do_socketcall(int num, int32_t *vptr) | ||
| 807 | int level = tswap32(vptr[1]); | 864 | int level = tswap32(vptr[1]); |
| 808 | int optname = tswap32(vptr[2]); | 865 | int optname = tswap32(vptr[2]); |
| 809 | void *optval = (void *)tswap32(vptr[3]); | 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 | break; | 871 | break; |
| 818 | default: | 872 | default: |
| @@ -823,6 +877,92 @@ static long do_socketcall(int num, int32_t *vptr) | @@ -823,6 +877,92 @@ static long do_socketcall(int num, int32_t *vptr) | ||
| 823 | return ret; | 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 | /* kernel structure types definitions */ | 966 | /* kernel structure types definitions */ |
| 827 | #define IFNAMSIZ 16 | 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,7 +2345,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, | ||
| 2205 | case TARGET_NR_sysinfo: | 2345 | case TARGET_NR_sysinfo: |
| 2206 | goto unimplemented; | 2346 | goto unimplemented; |
| 2207 | case TARGET_NR_ipc: | 2347 | case TARGET_NR_ipc: |
| 2208 | - goto unimplemented; | 2348 | + ret = do_ipc(arg1, arg2, arg3, arg4, arg5, arg6); |
| 2349 | + break; | ||
| 2209 | case TARGET_NR_fsync: | 2350 | case TARGET_NR_fsync: |
| 2210 | ret = get_errno(fsync(arg1)); | 2351 | ret = get_errno(fsync(arg1)); |
| 2211 | break; | 2352 | break; |
linux-user/syscall_defs.h
| @@ -24,6 +24,19 @@ | @@ -24,6 +24,19 @@ | ||
| 24 | #define SOCKOP_sendmsg 16 | 24 | #define SOCKOP_sendmsg 16 |
| 25 | #define SOCKOP_recvmsg 17 | 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 | * The following is for compatibility across the various Linux | 41 | * The following is for compatibility across the various Linux |
| 29 | * platforms. The i386 ioctl numbering scheme doesn't really enforce | 42 | * platforms. The i386 ioctl numbering scheme doesn't really enforce |