diff options
-rw-r--r-- | linux-user/syscall.c | 299 |
1 files changed, 250 insertions, 49 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 59c91f8da9..47d03f7f8d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -1924,14 +1924,12 @@ static abi_long do_socketcall(int num, abi_ulong vptr) } #endif -#ifdef TARGET_NR_ipc #define N_SHM_REGIONS 32 static struct shm_region { abi_ulong start; abi_ulong size; } shm_regions[N_SHM_REGIONS]; -#endif struct target_ipc_perm { @@ -2424,6 +2422,224 @@ end: return ret; } +struct target_shmid_ds +{ + struct target_ipc_perm shm_perm; + abi_ulong shm_segsz; + abi_ulong shm_atime; +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + int shm_cpid; + int shm_lpid; + abi_ulong shm_nattch; + unsigned long int __unused4; + unsigned long int __unused5; +}; + +static inline abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd, + abi_ulong target_addr) +{ + struct target_shmid_ds *target_sd; + + if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1)) + return -TARGET_EFAULT; + if (target_to_host_ipc_perm(&(host_sd->shm_perm), target_addr)) + return -TARGET_EFAULT; + __get_user(host_sd->shm_segsz, &target_sd->shm_segsz); + __get_user(host_sd->shm_atime, &target_sd->shm_atime); + __get_user(host_sd->shm_dtime, &target_sd->shm_dtime); + __get_user(host_sd->shm_ctime, &target_sd->shm_ctime); + __get_user(host_sd->shm_cpid, &target_sd->shm_cpid); + __get_user(host_sd->shm_lpid, &target_sd->shm_lpid); + __get_user(host_sd->shm_nattch, &target_sd->shm_nattch); + unlock_user_struct(target_sd, target_addr, 0); + return 0; +} + +static inline abi_long host_to_target_shmid_ds(abi_ulong target_addr, + struct shmid_ds *host_sd) +{ + struct target_shmid_ds *target_sd; + + if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0)) + return -TARGET_EFAULT; + if (host_to_target_ipc_perm(target_addr, &(host_sd->shm_perm))) + return -TARGET_EFAULT; + __put_user(host_sd->shm_segsz, &target_sd->shm_segsz); + __put_user(host_sd->shm_atime, &target_sd->shm_atime); + __put_user(host_sd->shm_dtime, &target_sd->shm_dtime); + __put_user(host_sd->shm_ctime, &target_sd->shm_ctime); + __put_user(host_sd->shm_cpid, &target_sd->shm_cpid); + __put_user(host_sd->shm_lpid, &target_sd->shm_lpid); + __put_user(host_sd->shm_nattch, &target_sd->shm_nattch); + unlock_user_struct(target_sd, target_addr, 1); + return 0; +} + +struct target_shminfo { + abi_ulong shmmax; + abi_ulong shmmin; + abi_ulong shmmni; + abi_ulong shmseg; + abi_ulong shmall; +}; + +static inline abi_long host_to_target_shminfo(abi_ulong target_addr, + struct shminfo *host_shminfo) +{ + struct target_shminfo *target_shminfo; + if (!lock_user_struct(VERIFY_WRITE, target_shminfo, target_addr, 0)) + return -TARGET_EFAULT; + __put_user(host_shminfo->shmmax, &target_shminfo->shmmax); + __put_user(host_shminfo->shmmin, &target_shminfo->shmmin); + __put_user(host_shminfo->shmmni, &target_shminfo->shmmni); + __put_user(host_shminfo->shmseg, &target_shminfo->shmseg); + __put_user(host_shminfo->shmall, &target_shminfo->shmall); + unlock_user_struct(target_shminfo, target_addr, 1); + return 0; +} + +struct target_shm_info { + int used_ids; + abi_ulong shm_tot; + abi_ulong shm_rss; + abi_ulong shm_swp; + abi_ulong swap_attempts; + abi_ulong swap_successes; +}; + +static inline abi_long host_to_target_shm_info(abi_ulong target_addr, + struct shm_info *host_shm_info) +{ + struct target_shm_info *target_shm_info; + if (!lock_user_struct(VERIFY_WRITE, target_shm_info, target_addr, 0)) + return -TARGET_EFAULT; + __put_user(host_shm_info->used_ids, &target_shm_info->used_ids); + __put_user(host_shm_info->shm_tot, &target_shm_info->shm_tot); + __put_user(host_shm_info->shm_rss, &target_shm_info->shm_rss); + __put_user(host_shm_info->shm_swp, &target_shm_info->shm_swp); + __put_user(host_shm_info->swap_attempts, &target_shm_info->swap_attempts); + __put_user(host_shm_info->swap_successes, &target_shm_info->swap_successes); + unlock_user_struct(target_shm_info, target_addr, 1); + return 0; +} + +static inline abi_long do_shmctl(int shmid, int cmd, abi_long buf) +{ + struct shmid_ds dsarg; + struct shminfo shminfo; + struct shm_info shm_info; + abi_long ret = -TARGET_EINVAL; + + cmd &= 0xff; + + switch(cmd) { + case IPC_STAT: + case IPC_SET: + case SHM_STAT: + if (target_to_host_shmid_ds(&dsarg, buf)) + return -TARGET_EFAULT; + ret = get_errno(shmctl(shmid, cmd, &dsarg)); + if (host_to_target_shmid_ds(buf, &dsarg)) + return -TARGET_EFAULT; + break; + case IPC_INFO: + ret = get_errno(shmctl(shmid, cmd, (struct shmid_ds *)&shminfo)); + if (host_to_target_shminfo(buf, &shminfo)) + return -TARGET_EFAULT; + break; + case SHM_INFO: + ret = get_errno(shmctl(shmid, cmd, (struct shmid_ds *)&shm_info)); + if (host_to_target_shm_info(buf, &shm_info)) + return -TARGET_EFAULT; + break; + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + ret = get_errno(shmctl(shmid, cmd, NULL)); + break; + } + + return ret; +} + +static inline abi_ulong do_shmat(int shmid, abi_ulong shmaddr, int shmflg) +{ + abi_long raddr; + void *host_raddr; + struct shmid_ds shm_info; + int i,ret; + + /* find out the length of the shared memory segment */ + ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info)); + if (is_error(ret)) { + /* can't get length, bail out */ + return ret; + } + + mmap_lock(); + + if (shmaddr) + host_raddr = shmat(shmid, (void *)g2h(shmaddr), shmflg); + else { + abi_ulong mmap_start; + + mmap_start = mmap_find_vma(0, shm_info.shm_segsz); + + if (mmap_start == -1) { + errno = ENOMEM; + host_raddr = (void *)-1; + } else + host_raddr = shmat(shmid, g2h(mmap_start), shmflg | SHM_REMAP); + } + + if (host_raddr == (void *)-1) { + mmap_unlock(); + return get_errno((long)host_raddr); + } + raddr=h2g((unsigned long)host_raddr); + + page_set_flags(raddr, raddr + shm_info.shm_segsz, + PAGE_VALID | PAGE_READ | + ((shmflg & SHM_RDONLY)? 0 : PAGE_WRITE)); + + for (i = 0; i < N_SHM_REGIONS; i++) { + if (shm_regions[i].start == 0) { + shm_regions[i].start = raddr; + shm_regions[i].size = shm_info.shm_segsz; + break; + } + } + + mmap_unlock(); + return raddr; + +} + +static inline abi_long do_shmdt(abi_ulong shmaddr) +{ + int i; + + for (i = 0; i < N_SHM_REGIONS; ++i) { + if (shm_regions[i].start == shmaddr) { + shm_regions[i].start = 0; + page_set_flags(shmaddr, shm_regions[i].size, 0); + break; + } + } + + return get_errno(shmdt(g2h(shmaddr))); +} + #ifdef TARGET_NR_ipc /* ??? This only works with linear mappings. */ /* do_ipc() must return target values and target errnos. */ @@ -2433,8 +2649,6 @@ static abi_long do_ipc(unsigned int call, int first, { int version; abi_long ret = 0; - struct shmid_ds shm_info; - int i; version = call >> 16; call &= 0xffff; @@ -2489,48 +2703,24 @@ static abi_long do_ipc(unsigned int call, int first, break; case IPCOP_shmat: + switch (version) { + default: { abi_ulong raddr; - void *host_addr; - /* SHM_* flags are the same on all linux platforms */ - host_addr = shmat(first, (void *)g2h(ptr), second); - if (host_addr == (void *)-1) { - ret = get_errno((long)host_addr); - break; - } - raddr = h2g((unsigned long)host_addr); - /* find out the length of the shared memory segment */ - - ret = get_errno(shmctl(first, IPC_STAT, &shm_info)); - if (is_error(ret)) { - /* can't get length, bail out */ - shmdt(host_addr); - break; - } - page_set_flags(raddr, raddr + shm_info.shm_segsz, - PAGE_VALID | PAGE_READ | - ((second & SHM_RDONLY)? 0: PAGE_WRITE)); - for (i = 0; i < N_SHM_REGIONS; ++i) { - if (shm_regions[i].start == 0) { - shm_regions[i].start = raddr; - shm_regions[i].size = shm_info.shm_segsz; - break; - } - } + raddr = do_shmat(first, ptr, second); + if (is_error(raddr)) + return get_errno(raddr); if (put_user_ual(raddr, third)) return -TARGET_EFAULT; - ret = 0; + break; + } + case 1: + ret = -TARGET_EINVAL; + break; } break; case IPCOP_shmdt: - for (i = 0; i < N_SHM_REGIONS; ++i) { - if (shm_regions[i].start == ptr) { - shm_regions[i].start = 0; - page_set_flags(ptr, shm_regions[i].size, 0); - break; - } - } - ret = get_errno(shmdt((void *)g2h(ptr))); + ret = do_shmdt(ptr); break; case IPCOP_shmget: @@ -2540,18 +2730,9 @@ static abi_long do_ipc(unsigned int call, int first, /* IPC_* and SHM_* command values are the same on all linux platforms */ case IPCOP_shmctl: - switch(second) { - case IPC_RMID: - case SHM_LOCK: - case SHM_UNLOCK: - ret = get_errno(shmctl(first, second, NULL)); - break; - default: - goto unimplemented; - } + ret = do_shmctl(first, second, third); break; default: - unimplemented: gemu_log("Unsupported ipc call: %d (version %d)\n", call, version); ret = -TARGET_ENOSYS; break; @@ -5312,6 +5493,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_msgsnd(arg1, arg2, arg3, arg4); break; #endif +#ifdef TARGET_NR_shmget + case TARGET_NR_shmget: + ret = get_errno(shmget(arg1, arg2, arg3)); + break; +#endif +#ifdef TARGET_NR_shmctl + case TARGET_NR_shmctl: + ret = do_shmctl(arg1, arg2, arg3); + break; +#endif +#ifdef TARGET_NR_shmat + case TARGET_NR_shmat: + ret = do_shmat(arg1, arg2, arg3); + break; +#endif +#ifdef TARGET_NR_shmdt + case TARGET_NR_shmdt: + ret = do_shmdt(arg1); + break; +#endif case TARGET_NR_fsync: ret = get_errno(fsync(arg1)); break; |