diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c index 612819c1b1..60077ce531 100644 --- a/linux-user/fd-trans.c +++ b/linux-user/fd-trans.c @@ -483,6 +483,12 @@ static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BR_ROOT_ID: case QEMU_IFLA_BR_BRIDGE_ID: break; + /* br_boolopt_multi { uint32_t, uint32_t } */ + case QEMU_IFLA_BR_MULTI_BOOLOPT: + u32 = NLA_DATA(nlattr); + u32[0] = tswap32(u32[0]); /* optval */ + u32[1] = tswap32(u32[1]); /* optmask */ + break; default: gemu_log("Unknown QEMU_IFLA_BR type %d\n", nlattr->nla_type); break; @@ -546,12 +552,6 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_ROOT_ID: case QEMU_IFLA_BRPORT_BRIDGE_ID: break; - /* br_boolopt_multi { uint32_t, uint32_t } */ - case QEMU_IFLA_BR_MULTI_BOOLOPT: - u32 = NLA_DATA(nlattr); - u32[0] = tswap32(u32[0]); /* optval */ - u32[1] = tswap32(u32[1]); /* optmask */ - break; default: gemu_log("Unknown QEMU_IFLA_BRPORT type %d\n", nlattr->nla_type); break; diff --git a/linux-user/generic/fcntl.h b/linux-user/generic/fcntl.h index a775a491e9..9f727d4df2 100644 --- a/linux-user/generic/fcntl.h +++ b/linux-user/generic/fcntl.h @@ -120,6 +120,7 @@ struct target_f_owner_ex { #define TARGET_F_SHLCK 8 #endif +#ifndef TARGET_HAVE_ARCH_STRUCT_FLOCK #ifndef TARGET_ARCH_FLOCK_PAD #define TARGET_ARCH_FLOCK_PAD #endif @@ -129,13 +130,12 @@ struct target_flock { short l_whence; abi_long l_start; abi_long l_len; -#if defined(TARGET_MIPS) - abi_long l_sysid; -#endif int l_pid; TARGET_ARCH_FLOCK_PAD }; +#endif +#ifndef TARGET_HAVE_ARCH_STRUCT_FLOCK64 #ifndef TARGET_ARCH_FLOCK64_PAD #define TARGET_ARCH_FLOCK64_PAD #endif @@ -149,3 +149,5 @@ struct target_flock64 { TARGET_ARCH_FLOCK64_PAD }; #endif + +#endif diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 43ba267547..0ba894fa7a 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -540,6 +540,23 @@ done_syscall: info.si_code = TARGET_ILL_ILLOPC; queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; + case EXCP_FPE: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTUNK; + if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INVALID) { + info.si_code = TARGET_FPE_FLTINV; + } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_DIV0) { + info.si_code = TARGET_FPE_FLTDIV; + } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_OVERFLOW) { + info.si_code = TARGET_FPE_FLTOVF; + } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_UNDERFLOW) { + info.si_code = TARGET_FPE_FLTUND; + } else if (GET_FP_CAUSE(env->active_fpu.fcr31) & FP_INEXACT) { + info.si_code = TARGET_FPE_FLTRES; + } + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; /* The code below was inspired by the MIPS Linux kernel trap * handling code in arch/mips/kernel/traps.c. */ diff --git a/linux-user/mips/target_fcntl.h b/linux-user/mips/target_fcntl.h index 000527cc95..6fc7b8a12b 100644 --- a/linux-user/mips/target_fcntl.h +++ b/linux-user/mips/target_fcntl.h @@ -27,8 +27,21 @@ #define TARGET_F_SETOWN 24 /* for sockets. */ #define TARGET_F_GETOWN 23 /* for sockets. */ -#define TARGET_ARCH_FLOCK_PAD abi_long pad[4]; -#define TARGET_ARCH_FLOCK64_PAD +#if (TARGET_ABI_BITS == 32) + +struct target_flock { + short l_type; + short l_whence; + abi_long l_start; + abi_long l_len; + abi_long l_sysid; + int l_pid; + abi_long pad[4]; +}; + +#define TARGET_HAVE_ARCH_STRUCT_FLOCK + +#endif #define TARGET_F_GETLK64 33 /* using 'struct flock64' */ #define TARGET_F_SETLK64 34 diff --git a/linux-user/strace.c b/linux-user/strace.c index 6f72a74c09..c80e93b5db 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -976,6 +976,76 @@ UNUSED static struct flags msg_flags[] = { FLAG_END, }; +UNUSED static struct flags statx_flags[] = { +#ifdef AT_EMPTY_PATH + FLAG_GENERIC(AT_EMPTY_PATH), +#endif +#ifdef AT_NO_AUTOMOUNT + FLAG_GENERIC(AT_NO_AUTOMOUNT), +#endif +#ifdef AT_SYMLINK_NOFOLLOW + FLAG_GENERIC(AT_SYMLINK_NOFOLLOW), +#endif +#ifdef AT_STATX_SYNC_AS_STAT + FLAG_GENERIC(AT_STATX_SYNC_AS_STAT), +#endif +#ifdef AT_STATX_FORCE_SYNC + FLAG_GENERIC(AT_STATX_FORCE_SYNC), +#endif +#ifdef AT_STATX_DONT_SYNC + FLAG_GENERIC(AT_STATX_DONT_SYNC), +#endif + FLAG_END, +}; + +UNUSED static struct flags statx_mask[] = { +/* This must come first, because it includes everything. */ +#ifdef STATX_ALL + FLAG_GENERIC(STATX_ALL), +#endif +/* This must come second; it includes everything except STATX_BTIME. */ +#ifdef STATX_BASIC_STATS + FLAG_GENERIC(STATX_BASIC_STATS), +#endif +#ifdef STATX_TYPE + FLAG_GENERIC(STATX_TYPE), +#endif +#ifdef STATX_MODE + FLAG_GENERIC(STATX_MODE), +#endif +#ifdef STATX_NLINK + FLAG_GENERIC(STATX_NLINK), +#endif +#ifdef STATX_UID + FLAG_GENERIC(STATX_UID), +#endif +#ifdef STATX_GID + FLAG_GENERIC(STATX_GID), +#endif +#ifdef STATX_ATIME + FLAG_GENERIC(STATX_ATIME), +#endif +#ifdef STATX_MTIME + FLAG_GENERIC(STATX_MTIME), +#endif +#ifdef STATX_CTIME + FLAG_GENERIC(STATX_CTIME), +#endif +#ifdef STATX_INO + FLAG_GENERIC(STATX_INO), +#endif +#ifdef STATX_SIZE + FLAG_GENERIC(STATX_SIZE), +#endif +#ifdef STATX_BLOCKS + FLAG_GENERIC(STATX_BLOCKS), +#endif +#ifdef STATX_BTIME + FLAG_GENERIC(STATX_BTIME), +#endif + FLAG_END, +}; + /* * print_xxx utility functions. These are used to print syscall * parameters in certain format. All of these have parameter @@ -2611,6 +2681,22 @@ print_tgkill(const struct syscallname *name, } #endif +#ifdef TARGET_NR_statx +static void +print_statx(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_at_dirfd(arg0, 0); + print_string(arg1, 0); + print_flags(statx_flags, arg2, 0); + print_flags(statx_mask, arg3, 0); + print_pointer(arg4, 1); + print_syscall_epilogue(name); +} +#endif + /* * An array of all of the syscalls we know about */ diff --git a/linux-user/strace.list b/linux-user/strace.list index db21ce4177..63a946642d 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1650,3 +1650,6 @@ #ifdef TARGET_NR_atomic_barrier { TARGET_NR_atomic_barrier, "atomic_barrier", NULL, NULL, NULL }, #endif +#ifdef TARGET_NR_statx +{ TARGET_NR_statx, "statx", NULL, print_statx, NULL }, +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index d2c9817938..39a37496fe 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -238,6 +238,7 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \ #define __NR_sys_inotify_init __NR_inotify_init #define __NR_sys_inotify_add_watch __NR_inotify_add_watch #define __NR_sys_inotify_rm_watch __NR_inotify_rm_watch +#define __NR_sys_statx __NR_statx #if defined(__alpha__) || defined(__x86_64__) || defined(__s390x__) #define __NR__llseek __NR_lseek @@ -316,6 +317,14 @@ _syscall5(int, kcmp, pid_t, pid1, pid_t, pid2, int, type, unsigned long, idx1, unsigned long, idx2) #endif +/* + * It is assumed that struct statx is architecture independent. + */ +#if defined(TARGET_NR_statx) && defined(__NR_statx) +_syscall5(int, sys_statx, int, dirfd, const char *, pathname, int, flags, + unsigned int, mask, struct target_statx *, statxbuf) +#endif + static bitmask_transtbl fcntl_flags_tbl[] = { { TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, }, { TARGET_O_ACCMODE, TARGET_O_RDWR, O_ACCMODE, O_RDWR, }, @@ -6516,6 +6525,48 @@ static inline abi_long host_to_target_stat64(void *cpu_env, } #endif +#if defined(TARGET_NR_statx) && defined(__NR_statx) +static inline abi_long host_to_target_statx(struct target_statx *host_stx, + abi_ulong target_addr) +{ + struct target_statx *target_stx; + + if (!lock_user_struct(VERIFY_WRITE, target_stx, target_addr, 0)) { + return -TARGET_EFAULT; + } + memset(target_stx, 0, sizeof(*target_stx)); + + __put_user(host_stx->stx_mask, &target_stx->stx_mask); + __put_user(host_stx->stx_blksize, &target_stx->stx_blksize); + __put_user(host_stx->stx_attributes, &target_stx->stx_attributes); + __put_user(host_stx->stx_nlink, &target_stx->stx_nlink); + __put_user(host_stx->stx_uid, &target_stx->stx_uid); + __put_user(host_stx->stx_gid, &target_stx->stx_gid); + __put_user(host_stx->stx_mode, &target_stx->stx_mode); + __put_user(host_stx->stx_ino, &target_stx->stx_ino); + __put_user(host_stx->stx_size, &target_stx->stx_size); + __put_user(host_stx->stx_blocks, &target_stx->stx_blocks); + __put_user(host_stx->stx_attributes_mask, &target_stx->stx_attributes_mask); + __put_user(host_stx->stx_atime.tv_sec, &target_stx->stx_atime.tv_sec); + __put_user(host_stx->stx_atime.tv_nsec, &target_stx->stx_atime.tv_nsec); + __put_user(host_stx->stx_btime.tv_sec, &target_stx->stx_atime.tv_sec); + __put_user(host_stx->stx_btime.tv_nsec, &target_stx->stx_atime.tv_nsec); + __put_user(host_stx->stx_ctime.tv_sec, &target_stx->stx_atime.tv_sec); + __put_user(host_stx->stx_ctime.tv_nsec, &target_stx->stx_atime.tv_nsec); + __put_user(host_stx->stx_mtime.tv_sec, &target_stx->stx_atime.tv_sec); + __put_user(host_stx->stx_mtime.tv_nsec, &target_stx->stx_atime.tv_nsec); + __put_user(host_stx->stx_rdev_major, &target_stx->stx_rdev_major); + __put_user(host_stx->stx_rdev_minor, &target_stx->stx_rdev_minor); + __put_user(host_stx->stx_dev_major, &target_stx->stx_dev_major); + __put_user(host_stx->stx_dev_minor, &target_stx->stx_dev_minor); + + unlock_user_struct(target_stx, target_addr, 1); + + return 0; +} +#endif + + /* ??? Using host futex calls even when target atomic operations are not really atomic probably breaks things. However implementing futexes locally would make futexes shared between multiple processes @@ -7094,7 +7145,8 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, abi_long ret; #if defined(TARGET_NR_stat) || defined(TARGET_NR_stat64) \ || defined(TARGET_NR_lstat) || defined(TARGET_NR_lstat64) \ - || defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64) + || defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64) \ + || defined(TARGET_NR_statx) struct stat st; #endif #if defined(TARGET_NR_statfs) || defined(TARGET_NR_statfs64) \ @@ -10172,6 +10224,67 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, ret = host_to_target_stat64(cpu_env, arg3, &st); return ret; #endif +#if defined(TARGET_NR_statx) + case TARGET_NR_statx: + { + struct target_statx *target_stx; + int dirfd = arg1; + int flags = arg3; + + p = lock_user_string(arg2); + if (p == NULL) { + return -TARGET_EFAULT; + } +#if defined(__NR_statx) + { + /* + * It is assumed that struct statx is architecture independent. + */ + struct target_statx host_stx; + int mask = arg4; + + ret = get_errno(sys_statx(dirfd, p, flags, mask, &host_stx)); + if (!is_error(ret)) { + if (host_to_target_statx(&host_stx, arg5) != 0) { + unlock_user(p, arg2, 0); + return -TARGET_EFAULT; + } + } + + if (ret != -TARGET_ENOSYS) { + unlock_user(p, arg2, 0); + return ret; + } + } +#endif + ret = get_errno(fstatat(dirfd, path(p), &st, flags)); + unlock_user(p, arg2, 0); + + if (!is_error(ret)) { + if (!lock_user_struct(VERIFY_WRITE, target_stx, arg5, 0)) { + return -TARGET_EFAULT; + } + memset(target_stx, 0, sizeof(*target_stx)); + __put_user(major(st.st_dev), &target_stx->stx_dev_major); + __put_user(minor(st.st_dev), &target_stx->stx_dev_minor); + __put_user(st.st_ino, &target_stx->stx_ino); + __put_user(st.st_mode, &target_stx->stx_mode); + __put_user(st.st_uid, &target_stx->stx_uid); + __put_user(st.st_gid, &target_stx->stx_gid); + __put_user(st.st_nlink, &target_stx->stx_nlink); + __put_user(major(st.st_rdev), &target_stx->stx_rdev_major); + __put_user(minor(st.st_rdev), &target_stx->stx_rdev_minor); + __put_user(st.st_size, &target_stx->stx_size); + __put_user(st.st_blksize, &target_stx->stx_blksize); + __put_user(st.st_blocks, &target_stx->stx_blocks); + __put_user(st.st_atime, &target_stx->stx_atime.tv_sec); + __put_user(st.st_mtime, &target_stx->stx_mtime.tv_sec); + __put_user(st.st_ctime, &target_stx->stx_ctime.tv_sec); + unlock_user_struct(target_stx, arg5, 1); + } + } + return ret; +#endif #ifdef TARGET_NR_lchown case TARGET_NR_lchown: if (!(p = lock_user_string(arg1))) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 3175440e9d..fffa89f256 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2537,4 +2537,41 @@ struct target_user_cap_data { /* Return size of the log buffer */ #define TARGET_SYSLOG_ACTION_SIZE_BUFFER 10 +struct target_statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t __reserved; +}; + +struct target_statx { + /* 0x00 */ + uint32_t stx_mask; /* What results were written [uncond] */ + uint32_t stx_blksize; /* Preferred general I/O size [uncond] */ + uint64_t stx_attributes; /* Flags conveying information about the file */ + /* 0x10 */ + uint32_t stx_nlink; /* Number of hard links */ + uint32_t stx_uid; /* User ID of owner */ + uint32_t stx_gid; /* Group ID of owner */ + uint16_t stx_mode; /* File mode */ + uint16_t __spare0[1]; + /* 0x20 */ + uint64_t stx_ino; /* Inode number */ + uint64_t stx_size; /* File size */ + uint64_t stx_blocks; /* Number of 512-byte blocks allocated */ + uint64_t stx_attributes_mask; /* Mask to show what is supported */ + /* 0x40 */ + struct target_statx_timestamp stx_atime; /* Last access time */ + struct target_statx_timestamp stx_btime; /* File creation time */ + struct target_statx_timestamp stx_ctime; /* Last attribute change time */ + struct target_statx_timestamp stx_mtime; /* Last data modification time */ + /* 0x80 */ + uint32_t stx_rdev_major; /* Device ID of special file [if bdev/cdev] */ + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; /* ID of device containing file [uncond] */ + uint32_t stx_dev_minor; + /* 0x90 */ + uint64_t __spare2[14]; /* Spare space for future expansion */ + /* 0x100 */ +}; + #endif