Implement shm* syscalls and fix 64/32bit errors

No regressions were observed on either 64bit or 32bit
IA hosts.

Patch based on original patches by:
  Kirill A. Shutemov <kirill@shutemov.name>
  - Implement shm* syscalls
  - Fix and cleanup IPCOP_shm* ipc calls handling

Depends on "export mmap_find_vma for shmat" patch.

Various whitespace uglifications applied to minimize
patch size.

Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
This commit is contained in:
Riku Voipio 2009-04-03 10:42:00 +03:00 committed by Riku Voipio
parent 9ad197d95c
commit 88a8c98455

View File

@ -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;
@ -5311,6 +5492,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
case TARGET_NR_msgsnd:
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));