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
This commit is contained in:
bellard 2004-02-22 14:57:26 +00:00
parent e374bfa35b
commit 8853f86e1d
2 changed files with 206 additions and 52 deletions

View File

@ -41,8 +41,10 @@
#include <sys/uio.h>
#include <sys/poll.h>
#include <sys/times.h>
#include <sys/shm.h>
#include <utime.h>
//#include <sys/user.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#define termios host_termios
@ -325,7 +327,7 @@ static inline void host_to_target_fds(target_long *target_fds,
target_long v;
if (target_fds) {
nw = n / TARGET_LONG_BITS;
nw = (n + TARGET_LONG_BITS - 1) / TARGET_LONG_BITS;
k = 0;
for(i = 0;i < nw; i++) {
v = 0;
@ -530,61 +532,116 @@ static inline void host_to_target_cmsg(struct target_msghdr *target_msgh,
static long do_setsockopt(int sockfd, int level, int optname,
void *optval, socklen_t optlen)
{
if (level == SOL_TCP) {
int val, ret;
switch(level) {
case SOL_TCP:
/* TCP options all take an 'int' value. */
int val;
if (optlen < sizeof(uint32_t))
return -EINVAL;
val = tswap32(*(uint32_t *)optval);
return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
}
else if (level != SOL_SOCKET) {
gemu_log("Unsupported setsockopt level: %d\n", level);
return -ENOSYS;
}
switch (optname) {
/* Options with 'int' argument. */
case SO_DEBUG:
case SO_REUSEADDR:
case SO_TYPE:
case SO_ERROR:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_KEEPALIVE:
case SO_OOBINLINE:
case SO_NO_CHECK:
case SO_PRIORITY:
case SO_BSDCOMPAT:
case SO_PASSCRED:
case SO_TIMESTAMP:
case SO_RCVLOWAT:
case SO_RCVTIMEO:
case SO_SNDTIMEO:
{
int val;
if (optlen < sizeof(uint32_t))
return -EINVAL;
val = tswap32(*(uint32_t *)optval);
return get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
}
if (get_user(val, (uint32_t *)optval))
return -EFAULT;
ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
break;
case SOL_IP:
switch(optname) {
case IP_HDRINCL:
val = 0;
if (optlen >= sizeof(uint32_t)) {
if (get_user(val, (uint32_t *)optval))
return -EFAULT;
} else if (optlen >= 1) {
if (get_user(val, (uint8_t *)optval))
return -EFAULT;
}
ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
break;
default:
goto unimplemented;
}
break;
case SOL_SOCKET:
switch (optname) {
/* Options with 'int' argument. */
case SO_DEBUG:
case SO_REUSEADDR:
case SO_TYPE:
case SO_ERROR:
case SO_DONTROUTE:
case SO_BROADCAST:
case SO_SNDBUF:
case SO_RCVBUF:
case SO_KEEPALIVE:
case SO_OOBINLINE:
case SO_NO_CHECK:
case SO_PRIORITY:
case SO_BSDCOMPAT:
case SO_PASSCRED:
case SO_TIMESTAMP:
case SO_RCVLOWAT:
case SO_RCVTIMEO:
case SO_SNDTIMEO:
if (optlen < sizeof(uint32_t))
return -EINVAL;
if (get_user(val, (uint32_t *)optval))
return -EFAULT;
ret = get_errno(setsockopt(sockfd, level, optname, &val, sizeof(val)));
break;
default:
goto unimplemented;
}
break;
default:
gemu_log("Unsupported setsockopt SOL_SOCKET option: %d\n", optname);
return -ENOSYS;
unimplemented:
gemu_log("Unsupported setsockopt level=%d optname=%d \n", level, optname);
ret = -ENOSYS;
}
return ret;
}
static long do_getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen)
{
gemu_log("getsockopt not yet supported\n");
return -ENOSYS;
int len, lv, val, ret;
switch(level) {
case SOL_SOCKET:
switch (optname) {
case SO_LINGER:
case SO_RCVTIMEO:
case SO_SNDTIMEO:
case SO_PEERCRED:
case SO_PEERNAME:
/* These don't just return a single integer */
goto unimplemented;
default:
if (get_user(len, optlen))
return -EFAULT;
if (len < 0)
return -EINVAL;
lv = sizeof(int);
ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv));
if (ret < 0)
return ret;
val = tswap32(val);
if (len > lv)
len = lv;
if (copy_to_user(optval, &val, len))
return -EFAULT;
if (put_user(len, optlen))
return -EFAULT;
break;
}
break;
default:
unimplemented:
gemu_log("getsockopt level=%d optname=%d not yet supported\n",
level, optname);
ret = -ENOSYS;
break;
}
return ret;
}
static long do_socketcall(int num, int32_t *vptr)
@ -807,12 +864,9 @@ static long do_socketcall(int num, int32_t *vptr)
int level = tswap32(vptr[1]);
int optname = tswap32(vptr[2]);
void *optval = (void *)tswap32(vptr[3]);
uint32_t *target_len = (void *)tswap32(vptr[4]);
socklen_t optlen = tswap32(*target_len);
uint32_t *poptlen = (void *)tswap32(vptr[4]);
ret = do_getsockopt(sockfd, level, optname, optval, &optlen);
if (!is_error(ret))
*target_len = tswap32(optlen);
ret = do_getsockopt(sockfd, level, optname, optval, poptlen);
}
break;
default:
@ -823,6 +877,92 @@ static long do_socketcall(int num, int32_t *vptr)
return ret;
}
#define N_SHM_REGIONS 32
static struct shm_region {
uint32_t start;
uint32_t size;
} shm_regions[N_SHM_REGIONS];
static long do_ipc(long call, long first, long second, long third,
long ptr, long fifth)
{
int version;
long ret = 0;
unsigned long raddr;
struct shmid_ds shm_info;
int i;
version = call >> 16;
call &= 0xffff;
switch (call) {
case IPCOP_shmat:
/* SHM_* flags are the same on all linux platforms */
ret = get_errno((long) shmat(first, (void *) ptr, second));
if (is_error(ret))
break;
raddr = ret;
/* 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((void *) raddr);
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;
}
}
if (put_user(raddr, (uint32_t *)third))
return -EFAULT;
ret = 0;
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 *) ptr));
break;
case IPCOP_shmget:
/* IPC_* flag values are the same on all linux platforms */
ret = get_errno(shmget(first, second, third));
break;
/* 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;
}
break;
default:
unimplemented:
gemu_log("Unsupported ipc call: %ld (version %d)\n", call, version);
ret = -ENOSYS;
break;
}
return ret;
}
/* kernel structure types definitions */
#define IFNAMSIZ 16
@ -2205,7 +2345,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_sysinfo:
goto unimplemented;
case TARGET_NR_ipc:
goto unimplemented;
ret = do_ipc(arg1, arg2, arg3, arg4, arg5, arg6);
break;
case TARGET_NR_fsync:
ret = get_errno(fsync(arg1));
break;

View File

@ -24,6 +24,19 @@
#define SOCKOP_sendmsg 16
#define SOCKOP_recvmsg 17
#define IPCOP_semop 1
#define IPCOP_semget 2
#define IPCOP_semctl 3
#define IPCOP_semtimedop 4
#define IPCOP_msgsnd 11
#define IPCOP_msgrcv 12
#define IPCOP_msgget 13
#define IPCOP_msgctl 14
#define IPCOP_shmat 21
#define IPCOP_shmdt 22
#define IPCOP_shmget 23
#define IPCOP_shmctl 24
/*
* The following is for compatibility across the various Linux
* platforms. The i386 ioctl numbering scheme doesn't really enforce