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 | ... | ... |