#include <fcntl.h>
#include <time.h>
#include <limits.h>
+#include <grp.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mount.h>
+#include <sys/file.h>
+#include <sys/fsuid.h>
+#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <linux/wireless.h>
+#include <linux/icmp.h>
#include "qemu-common.h"
#ifdef TARGET_GPROF
#include <sys/gmon.h>
#include <sys/epoll.h>
#endif
#ifdef CONFIG_ATTR
-#include "qemu-xattr.h"
+#include "qemu/xattr.h"
#endif
#define termios host_termios
#include <linux/fb.h>
#include <linux/vt.h>
#include <linux/dm-ioctl.h>
+#include <linux/reboot.h>
#include "linux_loop.h"
#include "cpu-uname.h"
struct host_rlimit64 *, old_limit)
#endif
-extern int personality(int);
-extern int flock(int, int);
-extern int setfsuid(int);
-extern int setfsgid(int);
-extern int setgroups(int, gid_t *);
-
/* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */
-#ifdef TARGET_ARM
+#ifdef TARGET_ARM
static inline int regpairs_aligned(void *cpu_env) {
return ((((CPUARMState *)cpu_env)->eabi) == 1) ;
}
#elif defined(TARGET_MIPS)
static inline int regpairs_aligned(void *cpu_env) { return 1; }
+#elif defined(TARGET_PPC) && !defined(TARGET_PPC64)
+/* SysV AVI for PPC32 expects 64bit parameters to be passed on odd/even pairs
+ * of registers which translates to the same as ARM/MIPS, because we start with
+ * r3 as arg1 */
+static inline int regpairs_aligned(void *cpu_env) { return 1; }
#else
static inline int regpairs_aligned(void *cpu_env) { return 0; }
#endif
target_cmsg->cmsg_type = tswap32(cmsg->cmsg_type);
target_cmsg->cmsg_len = tswapal(TARGET_CMSG_LEN(len));
- if (cmsg->cmsg_level != TARGET_SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
- gemu_log("Unsupported ancillary data: %d/%d\n", cmsg->cmsg_level, cmsg->cmsg_type);
- memcpy(target_data, data, len);
- } else {
+ if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) &&
+ (cmsg->cmsg_type == SCM_RIGHTS)) {
int *fd = (int *)data;
int *target_fd = (int *)target_data;
int i, numfds = len / sizeof(int);
for (i = 0; i < numfds; i++)
target_fd[i] = tswap32(fd[i]);
+ } else if ((cmsg->cmsg_level == TARGET_SOL_SOCKET) &&
+ (cmsg->cmsg_type == SO_TIMESTAMP) &&
+ (len == sizeof(struct timeval))) {
+ /* copy struct timeval to target */
+ struct timeval *tv = (struct timeval *)data;
+ struct target_timeval *target_tv =
+ (struct target_timeval *)target_data;
+
+ target_tv->tv_sec = tswapal(tv->tv_sec);
+ target_tv->tv_usec = tswapal(tv->tv_usec);
+ } else {
+ gemu_log("Unsupported ancillary data: %d/%d\n",
+ cmsg->cmsg_level, cmsg->cmsg_type);
+ memcpy(target_data, data, len);
}
cmsg = CMSG_NXTHDR(msgh, cmsg);
unlock_user (ip_mreq_source, optval_addr, 0);
break;
+ default:
+ goto unimplemented;
+ }
+ break;
+ case SOL_RAW:
+ switch (optname) {
+ case ICMP_FILTER:
+ /* struct icmp_filter takes an u32 value */
+ if (optlen < sizeof(uint32_t)) {
+ return -TARGET_EINVAL;
+ }
+
+ if (get_user_u32(val, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+ ret = get_errno(setsockopt(sockfd, level, optname,
+ &val, sizeof(val)));
+ break;
+
default:
goto unimplemented;
}
break;
case TARGET_SOL_SOCKET:
switch (optname) {
+ case TARGET_SO_RCVTIMEO:
+ {
+ struct timeval tv;
+
+ optname = SO_RCVTIMEO;
+
+set_timeout:
+ if (optlen != sizeof(struct target_timeval)) {
+ return -TARGET_EINVAL;
+ }
+
+ if (copy_from_user_timeval(&tv, optval_addr)) {
+ return -TARGET_EFAULT;
+ }
+
+ ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname,
+ &tv, sizeof(tv)));
+ return ret;
+ }
+ case TARGET_SO_SNDTIMEO:
+ optname = SO_SNDTIMEO;
+ goto set_timeout;
/* Options with 'int' argument. */
case TARGET_SO_DEBUG:
optname = SO_DEBUG;
case TARGET_SO_RCVLOWAT:
optname = SO_RCVLOWAT;
break;
- case TARGET_SO_RCVTIMEO:
- optname = SO_RCVTIMEO;
- break;
- case TARGET_SO_SNDTIMEO:
- optname = SO_SNDTIMEO;
- break;
break;
default:
goto unimplemented;
return ret;
}
-/* FIXME
- * lock_iovec()/unlock_iovec() have a return code of 0 for success where
- * other lock functions have a return code of 0 for failure.
- */
-static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
- int count, int copy)
+static struct iovec *lock_iovec(int type, abi_ulong target_addr,
+ int count, int copy)
{
struct target_iovec *target_vec;
- abi_ulong base;
+ struct iovec *vec;
+ abi_ulong total_len, max_len;
int i;
- target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
- if (!target_vec)
- return -TARGET_EFAULT;
- for(i = 0;i < count; i++) {
- base = tswapal(target_vec[i].iov_base);
- vec[i].iov_len = tswapal(target_vec[i].iov_len);
- if (vec[i].iov_len != 0) {
- vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
- /* Don't check lock_user return value. We must call writev even
- if a element has invalid base address. */
+ if (count == 0) {
+ errno = 0;
+ return NULL;
+ }
+ if (count > IOV_MAX) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ vec = calloc(count, sizeof(struct iovec));
+ if (vec == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ target_vec = lock_user(VERIFY_READ, target_addr,
+ count * sizeof(struct target_iovec), 1);
+ if (target_vec == NULL) {
+ errno = EFAULT;
+ goto fail2;
+ }
+
+ /* ??? If host page size > target page size, this will result in a
+ value larger than what we can actually support. */
+ max_len = 0x7fffffff & TARGET_PAGE_MASK;
+ total_len = 0;
+
+ for (i = 0; i < count; i++) {
+ abi_ulong base = tswapal(target_vec[i].iov_base);
+ abi_long len = tswapal(target_vec[i].iov_len);
+
+ if (len < 0) {
+ errno = EINVAL;
+ goto fail;
+ } else if (len == 0) {
+ /* Zero length pointer is ignored. */
+ vec[i].iov_base = 0;
} else {
- /* zero length pointer is ignored */
- vec[i].iov_base = NULL;
+ vec[i].iov_base = lock_user(type, base, len, copy);
+ if (!vec[i].iov_base) {
+ errno = EFAULT;
+ goto fail;
+ }
+ if (len > max_len - total_len) {
+ len = max_len - total_len;
+ }
}
+ vec[i].iov_len = len;
+ total_len += len;
}
- unlock_user (target_vec, target_addr, 0);
- return 0;
+
+ unlock_user(target_vec, target_addr, 0);
+ return vec;
+
+ fail:
+ free(vec);
+ fail2:
+ unlock_user(target_vec, target_addr, 0);
+ return NULL;
}
-static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
- int count, int copy)
+static void unlock_iovec(struct iovec *vec, abi_ulong target_addr,
+ int count, int copy)
{
struct target_iovec *target_vec;
- abi_ulong base;
int i;
- target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
- if (!target_vec)
- return -TARGET_EFAULT;
- for(i = 0;i < count; i++) {
- if (target_vec[i].iov_base) {
- base = tswapal(target_vec[i].iov_base);
+ target_vec = lock_user(VERIFY_READ, target_addr,
+ count * sizeof(struct target_iovec), 1);
+ if (target_vec) {
+ for (i = 0; i < count; i++) {
+ abi_ulong base = tswapal(target_vec[i].iov_base);
+ abi_long len = tswapal(target_vec[i].iov_base);
+ if (len < 0) {
+ break;
+ }
unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
}
+ unlock_user(target_vec, target_addr, 0);
}
- unlock_user (target_vec, target_addr, 0);
- return 0;
+ free(vec);
}
/* do_socket() Must return target values and target errnos. */
ret = target_to_host_sockaddr(msg.msg_name, tswapal(msgp->msg_name),
msg.msg_namelen);
if (ret) {
- unlock_user_struct(msgp, target_msg, send ? 0 : 1);
- return ret;
+ goto out2;
}
} else {
msg.msg_name = NULL;
msg.msg_flags = tswap32(msgp->msg_flags);
count = tswapal(msgp->msg_iovlen);
- vec = alloca(count * sizeof(struct iovec));
target_vec = tswapal(msgp->msg_iov);
- lock_iovec(send ? VERIFY_READ : VERIFY_WRITE, vec, target_vec, count, send);
+ vec = lock_iovec(send ? VERIFY_READ : VERIFY_WRITE,
+ target_vec, count, send);
+ if (vec == NULL) {
+ ret = -host_to_target_errno(errno);
+ goto out2;
+ }
msg.msg_iovlen = count;
msg.msg_iov = vec;
out:
unlock_iovec(vec, target_vec, count, !send);
+out2:
unlock_user_struct(msgp, target_msg, send ? 0 : 1);
return ret;
}
if (!lock_user_struct(VERIFY_WRITE, target_mb, msgp, 0))
return -TARGET_EFAULT;
- host_mb = malloc(msgsz+sizeof(long));
- ret = get_errno(msgrcv(msqid, host_mb, msgsz, tswapal(msgtyp), msgflg));
+ host_mb = g_malloc(msgsz+sizeof(long));
+ ret = get_errno(msgrcv(msqid, host_mb, msgsz, msgtyp, msgflg));
if (ret > 0) {
abi_ulong target_mtext_addr = msgp + sizeof(abi_ulong);
}
target_mb->mtype = tswapal(host_mb->mtype);
- free(host_mb);
end:
if (target_mb)
unlock_user_struct(target_mb, msgp, 1);
+ g_free(host_mb);
return ret;
}
break;
}
- ret = do_msgrcv(first, tmp->msgp, second, tmp->msgtyp, third);
+ ret = do_msgrcv(first, tswapal(tmp->msgp), second, tswapal(tmp->msgtyp), third);
unlock_user_struct(tmp, ptr, 0);
break;
unlock_user(argptr, arg, target_size);
}
out:
- if (big_buf) {
- free(big_buf);
- }
+ g_free(big_buf);
return ret;
}
return -TARGET_EINVAL;
}
+#define TRANSTBL_CONVERT(a) { -1, TARGET_##a, -1, a }
+static const bitmask_transtbl flock_tbl[] = {
+ TRANSTBL_CONVERT(F_RDLCK),
+ TRANSTBL_CONVERT(F_WRLCK),
+ TRANSTBL_CONVERT(F_UNLCK),
+ TRANSTBL_CONVERT(F_EXLCK),
+ TRANSTBL_CONVERT(F_SHLCK),
+ { 0, 0, 0, 0 }
+};
+
static abi_long do_fcntl(int fd, int cmd, abi_ulong arg)
{
struct flock fl;
case TARGET_F_GETLK:
if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1))
return -TARGET_EFAULT;
- fl.l_type = tswap16(target_fl->l_type);
+ fl.l_type =
+ target_to_host_bitmask(tswap16(target_fl->l_type), flock_tbl);
fl.l_whence = tswap16(target_fl->l_whence);
fl.l_start = tswapal(target_fl->l_start);
fl.l_len = tswapal(target_fl->l_len);
if (ret == 0) {
if (!lock_user_struct(VERIFY_WRITE, target_fl, arg, 0))
return -TARGET_EFAULT;
- target_fl->l_type = tswap16(fl.l_type);
+ target_fl->l_type =
+ host_to_target_bitmask(tswap16(fl.l_type), flock_tbl);
target_fl->l_whence = tswap16(fl.l_whence);
target_fl->l_start = tswapal(fl.l_start);
target_fl->l_len = tswapal(fl.l_len);
case TARGET_F_SETLKW:
if (!lock_user_struct(VERIFY_READ, target_fl, arg, 1))
return -TARGET_EFAULT;
- fl.l_type = tswap16(target_fl->l_type);
+ fl.l_type =
+ target_to_host_bitmask(tswap16(target_fl->l_type), flock_tbl);
fl.l_whence = tswap16(target_fl->l_whence);
fl.l_start = tswapal(target_fl->l_start);
fl.l_len = tswapal(target_fl->l_len);
case TARGET_F_GETLK64:
if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1))
return -TARGET_EFAULT;
- fl64.l_type = tswap16(target_fl64->l_type) >> 1;
+ fl64.l_type =
+ target_to_host_bitmask(tswap16(target_fl64->l_type), flock_tbl) >> 1;
fl64.l_whence = tswap16(target_fl64->l_whence);
fl64.l_start = tswap64(target_fl64->l_start);
fl64.l_len = tswap64(target_fl64->l_len);
if (ret == 0) {
if (!lock_user_struct(VERIFY_WRITE, target_fl64, arg, 0))
return -TARGET_EFAULT;
- target_fl64->l_type = tswap16(fl64.l_type) >> 1;
+ target_fl64->l_type =
+ host_to_target_bitmask(tswap16(fl64.l_type), flock_tbl) >> 1;
target_fl64->l_whence = tswap16(fl64.l_whence);
target_fl64->l_start = tswap64(fl64.l_start);
target_fl64->l_len = tswap64(fl64.l_len);
case TARGET_F_SETLKW64:
if (!lock_user_struct(VERIFY_READ, target_fl64, arg, 1))
return -TARGET_EFAULT;
- fl64.l_type = tswap16(target_fl64->l_type) >> 1;
+ fl64.l_type =
+ target_to_host_bitmask(tswap16(target_fl64->l_type), flock_tbl) >> 1;
fl64.l_whence = tswap16(target_fl64->l_whence);
fl64.l_start = tswap64(target_fl64->l_start);
fl64.l_len = tswap64(target_fl64->l_len);
/* Map host to target signal numbers for the wait family of syscalls.
Assume all other status bits are the same. */
-static int host_to_target_waitstatus(int status)
+int host_to_target_waitstatus(int status)
{
if (WIFSIGNALED(status)) {
return host_to_target_signal(WTERMSIG(status)) | (status & ~0x7f);
#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32)
dprintf(fd, "%08llx-%08llx rw-p %08llx 00:00 0 [stack]\n",
(unsigned long long)ts->info->stack_limit,
- (unsigned long long)(ts->stack_base + (TARGET_PAGE_SIZE - 1))
- & TARGET_PAGE_MASK,
+ (unsigned long long)(ts->info->start_stack +
+ (TARGET_PAGE_SIZE - 1)) & TARGET_PAGE_MASK,
(unsigned long long)0);
#endif
ret = get_errno(settimeofday(&tv, NULL));
}
break;
-#if defined(TARGET_NR_select) && !defined(TARGET_S390X) && !defined(TARGET_S390)
+#if defined(TARGET_NR_select)
case TARGET_NR_select:
+#if defined(TARGET_S390X) || defined(TARGET_ALPHA)
+ ret = do_select(arg1, arg2, arg3, arg4, arg5);
+#else
{
struct target_sel_arg_struct *sel;
abi_ulong inp, outp, exp, tvp;
unlock_user_struct(sel, arg1, 0);
ret = do_select(nsel, inp, outp, exp, tvp);
}
+#endif
break;
#endif
#ifdef TARGET_NR_pselect6
break;
#endif
case TARGET_NR_reboot:
- if (!(p = lock_user_string(arg4)))
- goto efault;
- ret = reboot(arg1, arg2, arg3, p);
- unlock_user(p, arg4, 0);
+ if (arg3 == LINUX_REBOOT_CMD_RESTART2) {
+ /* arg4 must be ignored in all other cases */
+ p = lock_user_string(arg4);
+ if (!p) {
+ goto efault;
+ }
+ ret = get_errno(reboot(arg1, arg2, arg3, p));
+ unlock_user(p, arg4, 0);
+ } else {
+ ret = get_errno(reboot(arg1, arg2, arg3, NULL));
+ }
break;
#ifdef TARGET_NR_readdir
case TARGET_NR_readdir:
__put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]);
__put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]);
__put_user(stfs.f_namelen, &target_stfs->f_namelen);
+ __put_user(stfs.f_frsize, &target_stfs->f_frsize);
+ memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare));
unlock_user_struct(target_stfs, arg2, 1);
}
break;
__put_user(stfs.f_fsid.__val[0], &target_stfs->f_fsid.val[0]);
__put_user(stfs.f_fsid.__val[1], &target_stfs->f_fsid.val[1]);
__put_user(stfs.f_namelen, &target_stfs->f_namelen);
+ __put_user(stfs.f_frsize, &target_stfs->f_frsize);
+ memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare));
unlock_user_struct(target_stfs, arg3, 1);
}
break;
ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4));
#elif defined(TARGET_CRIS)
ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg4, arg5));
+#elif defined(TARGET_MICROBLAZE)
+ ret = get_errno(do_fork(cpu_env, arg1, arg2, arg4, arg6, arg5));
#elif defined(TARGET_S390X)
ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg5, arg4));
#else
tde = target_dirp;
while (len > 0) {
reclen = de->d_reclen;
- treclen = reclen - (2 * (sizeof(long) - sizeof(abi_long)));
+ tnamelen = reclen - offsetof(struct linux_dirent, d_name);
+ assert(tnamelen >= 0);
+ treclen = tnamelen + offsetof(struct target_dirent, d_name);
+ assert(count1 + treclen <= count);
tde->d_reclen = tswap16(treclen);
tde->d_ino = tswapal(de->d_ino);
tde->d_off = tswapal(de->d_off);
- tnamelen = treclen - (2 * sizeof(abi_long) + 2);
- if (tnamelen > 256)
- tnamelen = 256;
- /* XXX: may not be correct */
- pstrcpy(tde->d_name, tnamelen, de->d_name);
+ memcpy(tde->d_name, de->d_name, tnamelen);
de = (struct linux_dirent *)((char *)de + reclen);
len -= reclen;
tde = (struct target_dirent *)((char *)tde + treclen);
}
break;
#endif /* TARGET_NR_getdents64 */
-#if defined(TARGET_NR__newselect) || defined(TARGET_S390X)
-#ifdef TARGET_S390X
- case TARGET_NR_select:
-#else
+#if defined(TARGET_NR__newselect)
case TARGET_NR__newselect:
-#endif
ret = do_select(arg1, arg2, arg3, arg4, arg5);
break;
#endif
break;
case TARGET_NR_readv:
{
- int count = arg3;
- struct iovec *vec;
-
- vec = alloca(count * sizeof(struct iovec));
- if (lock_iovec(VERIFY_WRITE, vec, arg2, count, 0) < 0)
- goto efault;
- ret = get_errno(readv(arg1, vec, count));
- unlock_iovec(vec, arg2, count, 1);
+ struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0);
+ if (vec != NULL) {
+ ret = get_errno(readv(arg1, vec, arg3));
+ unlock_iovec(vec, arg2, arg3, 1);
+ } else {
+ ret = -host_to_target_errno(errno);
+ }
}
break;
case TARGET_NR_writev:
{
- int count = arg3;
- struct iovec *vec;
-
- vec = alloca(count * sizeof(struct iovec));
- if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
- goto efault;
- ret = get_errno(writev(arg1, vec, count));
- unlock_iovec(vec, arg2, count, 0);
+ struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
+ if (vec != NULL) {
+ ret = get_errno(writev(arg1, vec, arg3));
+ unlock_iovec(vec, arg2, arg3, 0);
+ } else {
+ ret = -host_to_target_errno(errno);
+ }
}
break;
case TARGET_NR_getsid:
goto unimplemented;
#endif
#endif
-#ifdef TARGET_NR_pread
- case TARGET_NR_pread:
- if (regpairs_aligned(cpu_env))
- arg4 = arg5;
- if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
- goto efault;
- ret = get_errno(pread(arg1, p, arg3, arg4));
- unlock_user(p, arg2, ret);
- break;
- case TARGET_NR_pwrite:
- if (regpairs_aligned(cpu_env))
- arg4 = arg5;
- if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
- goto efault;
- ret = get_errno(pwrite(arg1, p, arg3, arg4));
- unlock_user(p, arg2, 0);
- break;
-#endif
#ifdef TARGET_NR_pread64
case TARGET_NR_pread64:
+ if (regpairs_aligned(cpu_env)) {
+ arg4 = arg5;
+ arg5 = arg6;
+ }
if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
goto efault;
ret = get_errno(pread64(arg1, p, arg3, target_offset64(arg4, arg5)));
unlock_user(p, arg2, ret);
break;
case TARGET_NR_pwrite64:
+ if (regpairs_aligned(cpu_env)) {
+ arg4 = arg5;
+ arg5 = arg6;
+ }
if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
goto efault;
ret = get_errno(pwrite64(arg1, p, arg3, target_offset64(arg4, arg5)));
#ifdef TARGET_NR_vmsplice
case TARGET_NR_vmsplice:
{
- int count = arg3;
- struct iovec *vec;
-
- vec = alloca(count * sizeof(struct iovec));
- if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
- goto efault;
- ret = get_errno(vmsplice(arg1, vec, count, arg4));
- unlock_iovec(vec, arg2, count, 0);
+ struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1);
+ if (vec != NULL) {
+ ret = get_errno(vmsplice(arg1, vec, arg3, arg4));
+ unlock_iovec(vec, arg2, arg3, 0);
+ } else {
+ ret = -host_to_target_errno(errno);
+ }
}
break;
#endif
}
break;
}
+#endif
+#ifdef TARGET_NR_gethostname
+ case TARGET_NR_gethostname:
+ {
+ char *name = lock_user(VERIFY_WRITE, arg1, arg2, 0);
+ if (name) {
+ ret = get_errno(gethostname(name, arg2));
+ unlock_user(name, arg1, arg2);
+ } else {
+ ret = -TARGET_EFAULT;
+ }
+ break;
+ }
#endif
default:
unimplemented: