summaryrefslogtreecommitdiffstats
path: root/linux-user
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/aarch64/signal.c4
-rw-r--r--linux-user/elfload.c95
-rw-r--r--linux-user/ioctls.h46
-rw-r--r--linux-user/mips/cpu_loop.c75
-rw-r--r--linux-user/mips/target_elf.h3
-rw-r--r--linux-user/mips/target_syscall.h6
-rw-r--r--linux-user/mips64/target_syscall.h6
-rw-r--r--linux-user/qemu.h80
-rw-r--r--linux-user/sparc/signal.c4
-rw-r--r--linux-user/syscall.c252
-rw-r--r--linux-user/syscall_defs.h28
-rw-r--r--linux-user/syscall_types.h68
12 files changed, 609 insertions, 58 deletions
diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c
index 07fedfc33c..f84a9cf28a 100644
--- a/linux-user/aarch64/signal.c
+++ b/linux-user/aarch64/signal.c
@@ -314,7 +314,7 @@ static int target_restore_sigframe(CPUARMState *env,
break;
case TARGET_SVE_MAGIC:
- if (arm_feature(env, ARM_FEATURE_SVE)) {
+ if (cpu_isar_feature(aa64_sve, arm_env_get_cpu(env))) {
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
if (!sve && size == sve_size) {
@@ -433,7 +433,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
&layout);
/* SVE state needs saving only if it exists. */
- if (arm_feature(env, ARM_FEATURE_SVE)) {
+ if (cpu_isar_feature(aa64_sve, arm_env_get_cpu(env))) {
vq = (env->vfp.zcr_el[1] & 0xf) + 1;
sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16);
sve_ofs = alloc_sigframe_space(sve_size, &layout);
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 10bca65b99..5bccd2e243 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -458,6 +458,10 @@ static uint32_t get_elf_hwcap(void)
/* probe for the extra features */
#define GET_FEATURE(feat, hwcap) \
do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0)
+
+#define GET_FEATURE_ID(feat, hwcap) \
+ do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0)
+
/* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */
GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP);
GET_FEATURE(ARM_FEATURE_VFP, ARM_HWCAP_ARM_VFP);
@@ -467,8 +471,8 @@ static uint32_t get_elf_hwcap(void)
GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPv3);
GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS);
GET_FEATURE(ARM_FEATURE_VFP4, ARM_HWCAP_ARM_VFPv4);
- GET_FEATURE(ARM_FEATURE_ARM_DIV, ARM_HWCAP_ARM_IDIVA);
- GET_FEATURE(ARM_FEATURE_THUMB_DIV, ARM_HWCAP_ARM_IDIVT);
+ GET_FEATURE_ID(arm_div, ARM_HWCAP_ARM_IDIVA);
+ GET_FEATURE_ID(thumb_div, ARM_HWCAP_ARM_IDIVT);
/* All QEMU's VFPv3 CPUs have 32 registers, see VFP_DREG in translate.c.
* Note that the ARM_HWCAP_ARM_VFPv3D16 bit is always the inverse of
* ARM_HWCAP_ARM_VFPD32 (and so always clear for QEMU); it is unrelated
@@ -485,15 +489,16 @@ static uint32_t get_elf_hwcap2(void)
ARMCPU *cpu = ARM_CPU(thread_cpu);
uint32_t hwcaps = 0;
- GET_FEATURE(ARM_FEATURE_V8_AES, ARM_HWCAP2_ARM_AES);
- GET_FEATURE(ARM_FEATURE_V8_PMULL, ARM_HWCAP2_ARM_PMULL);
- GET_FEATURE(ARM_FEATURE_V8_SHA1, ARM_HWCAP2_ARM_SHA1);
- GET_FEATURE(ARM_FEATURE_V8_SHA256, ARM_HWCAP2_ARM_SHA2);
- GET_FEATURE(ARM_FEATURE_CRC, ARM_HWCAP2_ARM_CRC32);
+ GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES);
+ GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL);
+ GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1);
+ GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2);
+ GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32);
return hwcaps;
}
#undef GET_FEATURE
+#undef GET_FEATURE_ID
#else
/* 64 bit ARM definitions */
@@ -568,25 +573,26 @@ static uint32_t get_elf_hwcap(void)
hwcaps |= ARM_HWCAP_A64_ASIMD;
/* probe for the extra features */
-#define GET_FEATURE(feat, hwcap) \
- do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0)
- GET_FEATURE(ARM_FEATURE_V8_AES, ARM_HWCAP_A64_AES);
- GET_FEATURE(ARM_FEATURE_V8_PMULL, ARM_HWCAP_A64_PMULL);
- GET_FEATURE(ARM_FEATURE_V8_SHA1, ARM_HWCAP_A64_SHA1);
- GET_FEATURE(ARM_FEATURE_V8_SHA256, ARM_HWCAP_A64_SHA2);
- GET_FEATURE(ARM_FEATURE_CRC, ARM_HWCAP_A64_CRC32);
- GET_FEATURE(ARM_FEATURE_V8_SHA3, ARM_HWCAP_A64_SHA3);
- GET_FEATURE(ARM_FEATURE_V8_SM3, ARM_HWCAP_A64_SM3);
- GET_FEATURE(ARM_FEATURE_V8_SM4, ARM_HWCAP_A64_SM4);
- GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512);
- GET_FEATURE(ARM_FEATURE_V8_FP16,
- ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP);
- GET_FEATURE(ARM_FEATURE_V8_ATOMICS, ARM_HWCAP_A64_ATOMICS);
- GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM);
- GET_FEATURE(ARM_FEATURE_V8_DOTPROD, ARM_HWCAP_A64_ASIMDDP);
- GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA);
- GET_FEATURE(ARM_FEATURE_SVE, ARM_HWCAP_A64_SVE);
-#undef GET_FEATURE
+#define GET_FEATURE_ID(feat, hwcap) \
+ do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0)
+
+ GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES);
+ GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL);
+ GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1);
+ GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2);
+ GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512);
+ GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32);
+ GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3);
+ GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3);
+ GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4);
+ GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP);
+ GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS);
+ GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM);
+ GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP);
+ GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA);
+ GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE);
+
+#undef GET_FEATURE_ID
return hwcaps;
}
@@ -1511,11 +1517,25 @@ static void bswap_sym(struct elf_sym *sym)
bswaptls(&sym->st_size);
bswap16s(&sym->st_shndx);
}
+
+#ifdef TARGET_MIPS
+static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags)
+{
+ bswap16s(&abiflags->version);
+ bswap32s(&abiflags->ases);
+ bswap32s(&abiflags->isa_ext);
+ bswap32s(&abiflags->flags1);
+ bswap32s(&abiflags->flags2);
+}
+#endif
#else
static inline void bswap_ehdr(struct elfhdr *ehdr) { }
static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { }
static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { }
static inline void bswap_sym(struct elf_sym *sym) { }
+#ifdef TARGET_MIPS
+static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { }
+#endif
#endif
#ifdef USE_ELF_CORE_DUMP
@@ -2358,6 +2378,26 @@ static void load_elf_image(const char *image_name, int image_fd,
goto exit_errmsg;
}
*pinterp_name = interp_name;
+#ifdef TARGET_MIPS
+ } else if (eppnt->p_type == PT_MIPS_ABIFLAGS) {
+ Mips_elf_abiflags_v0 abiflags;
+ if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) {
+ errmsg = "Invalid PT_MIPS_ABIFLAGS entry";
+ goto exit_errmsg;
+ }
+ if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
+ memcpy(&abiflags, bprm_buf + eppnt->p_offset,
+ sizeof(Mips_elf_abiflags_v0));
+ } else {
+ retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0),
+ eppnt->p_offset);
+ if (retval != sizeof(Mips_elf_abiflags_v0)) {
+ goto exit_perror;
+ }
+ }
+ bswap_mips_abiflags(&abiflags);
+ info->fp_abi = abiflags.fp_abi;
+#endif
}
}
@@ -2669,6 +2709,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
+#ifdef TARGET_MIPS
+ info->interp_fp_abi = interp_info.fp_abi;
+#endif
}
bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex,
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h
index 586c794639..ae8951625f 100644
--- a/linux-user/ioctls.h
+++ b/linux-user/ioctls.h
@@ -131,6 +131,52 @@
IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT))
IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT))
+#ifdef CONFIG_USBFS
+ /* USB ioctls */
+ IOCTL(USBDEVFS_CONTROL, IOC_RW,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ctrltransfer)))
+ IOCTL(USBDEVFS_BULK, IOC_RW,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_bulktransfer)))
+ IOCTL(USBDEVFS_RESETEP, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_SETINTERFACE, IOC_W,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_setinterface)))
+ IOCTL(USBDEVFS_SETCONFIGURATION, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_GETDRIVER, IOC_R,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_getdriver)))
+ IOCTL_SPECIAL(USBDEVFS_SUBMITURB, IOC_W, do_ioctl_usbdevfs_submiturb,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb)))
+ IOCTL_SPECIAL(USBDEVFS_DISCARDURB, IOC_RW, do_ioctl_usbdevfs_discardurb,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb)))
+ IOCTL_SPECIAL(USBDEVFS_REAPURB, IOC_R, do_ioctl_usbdevfs_reapurb,
+ MK_PTR(TYPE_PTRVOID))
+ IOCTL_SPECIAL(USBDEVFS_REAPURBNDELAY, IOC_R, do_ioctl_usbdevfs_reapurb,
+ MK_PTR(TYPE_PTRVOID))
+ IOCTL(USBDEVFS_DISCSIGNAL, IOC_W,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnectsignal)))
+ IOCTL(USBDEVFS_CLAIMINTERFACE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_RELEASEINTERFACE, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_CONNECTINFO, IOC_R,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_connectinfo)))
+ IOCTL(USBDEVFS_IOCTL, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ioctl)))
+ IOCTL(USBDEVFS_HUB_PORTINFO, IOC_R,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_hub_portinfo)))
+ IOCTL(USBDEVFS_RESET, 0, TYPE_NULL)
+ IOCTL(USBDEVFS_CLEAR_HALT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_DISCONNECT, 0, TYPE_NULL)
+ IOCTL(USBDEVFS_CONNECT, 0, TYPE_NULL)
+ IOCTL(USBDEVFS_CLAIM_PORT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_RELEASE_PORT, IOC_W, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_GET_CAPABILITIES, IOC_R, MK_PTR(TYPE_INT))
+ IOCTL(USBDEVFS_DISCONNECT_CLAIM, IOC_W,
+ MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnect_claim)))
+#ifdef USBDEVFS_DROP_PRIVILEGES
+ IOCTL(USBDEVFS_DROP_PRIVILEGES, IOC_W, MK_PTR(TYPE_INT))
+#endif
+#ifdef USBDEVFS_GET_SPEED
+ IOCTL(USBDEVFS_GET_SPEED, 0, TYPE_NULL)
+#endif
+#endif /* CONFIG_USBFS */
+
IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT))
IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT))
IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq)))
diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c
index c9c20cf8b7..97e495747f 100644
--- a/linux-user/mips/cpu_loop.c
+++ b/linux-user/mips/cpu_loop.c
@@ -740,6 +740,34 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
struct image_info *info = ts->info;
int i;
+ struct mode_req {
+ bool single;
+ bool soft;
+ bool fr1;
+ bool frdefault;
+ bool fre;
+ };
+
+ static const struct mode_req fpu_reqs[] = {
+ [MIPS_ABI_FP_ANY] = { true, true, true, true, true },
+ [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true },
+ [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false },
+ [MIPS_ABI_FP_SOFT] = { false, true, false, false, false },
+ [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false },
+ [MIPS_ABI_FP_XX] = { false, false, true, true, true },
+ [MIPS_ABI_FP_64] = { false, false, true, false, false },
+ [MIPS_ABI_FP_64A] = { false, false, true, false, true }
+ };
+
+ /*
+ * Mode requirements when .MIPS.abiflags is not present in the ELF.
+ * Not present means that everything is acceptable except FR1.
+ */
+ static struct mode_req none_req = { true, true, false, true, true };
+
+ struct mode_req prog_req;
+ struct mode_req interp_req;
+
for(i = 0; i < 32; i++) {
env->active_tc.gpr[i] = regs->regs[i];
}
@@ -747,6 +775,53 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
if (regs->cp0_epc & 1) {
env->hflags |= MIPS_HFLAG_M16;
}
+
+#ifdef TARGET_ABI_MIPSO32
+# define MAX_FP_ABI MIPS_ABI_FP_64A
+#else
+# define MAX_FP_ABI MIPS_ABI_FP_SOFT
+#endif
+ if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN)
+ || (info->interp_fp_abi > MAX_FP_ABI &&
+ info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) {
+ fprintf(stderr, "qemu: Unexpected FPU mode\n");
+ exit(1);
+ }
+
+ prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
+ : fpu_reqs[info->fp_abi];
+ interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req
+ : fpu_reqs[info->interp_fp_abi];
+
+ prog_req.single &= interp_req.single;
+ prog_req.soft &= interp_req.soft;
+ prog_req.fr1 &= interp_req.fr1;
+ prog_req.frdefault &= interp_req.frdefault;
+ prog_req.fre &= interp_req.fre;
+
+ bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS32R2 ||
+ env->insn_flags & ISA_MIPS64R2 ||
+ env->insn_flags & ISA_MIPS32R6 ||
+ env->insn_flags & ISA_MIPS64R6;
+
+ if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) {
+ env->CP0_Config5 |= (1 << CP0C5_FRE);
+ if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
+ env->hflags |= MIPS_HFLAG_FRE;
+ }
+ } else if ((prog_req.fr1 && prog_req.frdefault) ||
+ (prog_req.single && !prog_req.frdefault)) {
+ if ((env->active_fpu.fcr0 & (1 << FCR0_F64)
+ && cpu_has_mips_r2_r6) || prog_req.fr1) {
+ env->CP0_Status |= (1 << CP0St_FR);
+ env->hflags |= MIPS_HFLAG_F64;
+ }
+ } else if (!prog_req.fre && !prog_req.frdefault &&
+ !prog_req.fr1 && !prog_req.single && !prog_req.soft) {
+ fprintf(stderr, "qemu: Can't find a matching FPU mode\n");
+ exit(1);
+ }
+
if (env->insn_flags & ISA_NANOMIPS32) {
return;
}
diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h
index fa5d30bf99..a98c9bd6ad 100644
--- a/linux-user/mips/target_elf.h
+++ b/linux-user/mips/target_elf.h
@@ -12,6 +12,9 @@ static inline const char *cpu_get_model(uint32_t eflags)
if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) {
return "mips32r6-generic";
}
+ if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) {
+ return "R5900";
+ }
return "24Kf";
}
#endif
diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h
index 2fca1c6bf9..d5509a34a7 100644
--- a/linux-user/mips/target_syscall.h
+++ b/linux-user/mips/target_syscall.h
@@ -244,4 +244,10 @@ static inline abi_ulong target_shmlba(CPUMIPSState *env)
return 0x40000;
}
+/* MIPS-specific prctl() options */
+#define TARGET_PR_SET_FP_MODE 45
+#define TARGET_PR_GET_FP_MODE 46
+#define TARGET_PR_FP_MODE_FR (1 << 0)
+#define TARGET_PR_FP_MODE_FRE (1 << 1)
+
#endif /* MIPS_TARGET_SYSCALL_H */
diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h
index 078437d765..8ccc46822c 100644
--- a/linux-user/mips64/target_syscall.h
+++ b/linux-user/mips64/target_syscall.h
@@ -241,4 +241,10 @@ static inline abi_ulong target_shmlba(CPUMIPSState *env)
return 0x40000;
}
+/* MIPS-specific prctl() options */
+#define TARGET_PR_SET_FP_MODE 45
+#define TARGET_PR_GET_FP_MODE 46
+#define TARGET_PR_FP_MODE_FR (1 << 0)
+#define TARGET_PR_FP_MODE_FRE (1 << 1)
+
#endif /* MIPS64_TARGET_SYSCALL_H */
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index b4959e41c6..dd5771ce0c 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -61,6 +61,10 @@ struct image_info {
abi_ulong interpreter_loadmap_addr;
abi_ulong interpreter_pt_dynamic_addr;
struct image_info *other_info;
+#ifdef TARGET_MIPS
+ int fp_abi;
+ int interp_fp_abi;
+#endif
};
#ifdef TARGET_I386
@@ -143,7 +147,7 @@ typedef struct TaskState {
/* Nonzero if process_pending_signals() needs to do something (either
* handle a pending signal or unblock signals).
* This flag is written from a signal handler so should be accessed via
- * the atomic_read() and atomic_write() functions. (It is not accessed
+ * the atomic_read() and atomic_set() functions. (It is not accessed
* from multiple threads.)
*/
int signal_pending;
@@ -461,27 +465,59 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size)
These are usually used to access struct data members once the struct has
been locked - usually with lock_user_struct. */
-/* Tricky points:
- - Use __builtin_choose_expr to avoid type promotion from ?:,
- - Invalid sizes result in a compile time error stemming from
- the fact that abort has no parameters.
- - It's easier to use the endian-specific unaligned load/store
- functions than host-endian unaligned load/store plus tswapN. */
-
-#define __put_user_e(x, hptr, e) \
- (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \
- __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \
- __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \
- __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \
- ((hptr), (x)), (void)0)
-
-#define __get_user_e(x, hptr, e) \
- ((x) = (typeof(*hptr))( \
- __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \
- __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \
- __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \
- __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \
- (hptr)), (void)0)
+/*
+ * Tricky points:
+ * - Use __builtin_choose_expr to avoid type promotion from ?:,
+ * - Invalid sizes result in a compile time error stemming from
+ * the fact that abort has no parameters.
+ * - It's easier to use the endian-specific unaligned load/store
+ * functions than host-endian unaligned load/store plus tswapN.
+ * - The pragmas are necessary only to silence a clang false-positive
+ * warning: see https://bugs.llvm.org/show_bug.cgi?id=39113 .
+ * - We have to disable -Wpragmas warnings to avoid a complaint about
+ * an unknown warning type from older compilers that don't know about
+ * -Waddress-of-packed-member.
+ * - gcc has bugs in its _Pragma() support in some versions, eg
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83256 -- so we only
+ * include the warning-suppression pragmas for clang
+ */
+#ifdef __clang__
+#define PRAGMA_DISABLE_PACKED_WARNING \
+ _Pragma("GCC diagnostic push"); \
+ _Pragma("GCC diagnostic ignored \"-Wpragmas\""); \
+ _Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"")
+
+#define PRAGMA_REENABLE_PACKED_WARNING \
+ _Pragma("GCC diagnostic pop")
+
+#else
+#define PRAGMA_DISABLE_PACKED_WARNING
+#define PRAGMA_REENABLE_PACKED_WARNING
+#endif
+
+#define __put_user_e(x, hptr, e) \
+ do { \
+ PRAGMA_DISABLE_PACKED_WARNING; \
+ (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \
+ __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \
+ __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \
+ __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \
+ ((hptr), (x)), (void)0); \
+ PRAGMA_REENABLE_PACKED_WARNING; \
+ } while (0)
+
+#define __get_user_e(x, hptr, e) \
+ do { \
+ PRAGMA_DISABLE_PACKED_WARNING; \
+ ((x) = (typeof(*hptr))( \
+ __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \
+ __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \
+ __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \
+ __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \
+ (hptr)), (void)0); \
+ PRAGMA_REENABLE_PACKED_WARNING; \
+ } while (0)
+
#ifdef TARGET_WORDS_BIGENDIAN
# define __put_user(x, hptr) __put_user_e(x, hptr, be)
diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c
index b4c60aa446..e44e99993c 100644
--- a/linux-user/sparc/signal.c
+++ b/linux-user/sparc/signal.c
@@ -258,10 +258,6 @@ void setup_frame(int sig, struct target_sigaction *ka,
__put_user(val32, &sf->insns[1]);
if (err)
goto sigsegv;
-
- /* Flush instruction space. */
- // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0]));
- // tb_flush(env);
}
unlock_user(sf, sf_addr, sizeof(struct target_signal_frame));
return;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index ae3c0dfef7..810a58b704 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -94,6 +94,10 @@
#include <linux/fiemap.h>
#endif
#include <linux/fb.h>
+#if defined(CONFIG_USBFS)
+#include <linux/usbdevice_fs.h>
+#include <linux/usb/ch9.h>
+#endif
#include <linux/vt.h>
#include <linux/dm-ioctl.h>
#include <linux/reboot.h>
@@ -4196,6 +4200,182 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp,
return ret;
}
+#if defined(CONFIG_USBFS)
+#if HOST_LONG_BITS > 64
+#error USBDEVFS thunks do not support >64 bit hosts yet.
+#endif
+struct live_urb {
+ uint64_t target_urb_adr;
+ uint64_t target_buf_adr;
+ char *target_buf_ptr;
+ struct usbdevfs_urb host_urb;
+};
+
+static GHashTable *usbdevfs_urb_hashtable(void)
+{
+ static GHashTable *urb_hashtable;
+
+ if (!urb_hashtable) {
+ urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal);
+ }
+ return urb_hashtable;
+}
+
+static void urb_hashtable_insert(struct live_urb *urb)
+{
+ GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+ g_hash_table_insert(urb_hashtable, urb, urb);
+}
+
+static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr)
+{
+ GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+ return g_hash_table_lookup(urb_hashtable, &target_urb_adr);
+}
+
+static void urb_hashtable_remove(struct live_urb *urb)
+{
+ GHashTable *urb_hashtable = usbdevfs_urb_hashtable();
+ g_hash_table_remove(urb_hashtable, urb);
+}
+
+static abi_long
+do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, int cmd, abi_long arg)
+{
+ const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) };
+ const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 };
+ struct live_urb *lurb;
+ void *argptr;
+ uint64_t hurb;
+ int target_size;
+ uintptr_t target_urb_adr;
+ abi_long ret;
+
+ target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET);
+
+ memset(buf_temp, 0, sizeof(uint64_t));
+ ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp));
+ if (is_error(ret)) {
+ return ret;
+ }
+
+ memcpy(&hurb, buf_temp, sizeof(uint64_t));
+ lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb));
+ if (!lurb->target_urb_adr) {
+ return -TARGET_EFAULT;
+ }
+ urb_hashtable_remove(lurb);
+ unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr,
+ lurb->host_urb.buffer_length);
+ lurb->target_buf_ptr = NULL;
+
+ /* restore the guest buffer pointer */
+ lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr;
+
+ /* update the guest urb struct */
+ argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0);
+ if (!argptr) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+ thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET);
+ unlock_user(argptr, lurb->target_urb_adr, target_size);
+
+ target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET);
+ /* write back the urb handle */
+ argptr = lock_user(VERIFY_WRITE, arg, target_size, 0);
+ if (!argptr) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+
+ /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */
+ target_urb_adr = lurb->target_urb_adr;
+ thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET);
+ unlock_user(argptr, arg, target_size);
+
+ g_free(lurb);
+ return ret;
+}
+
+static abi_long
+do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie,
+ uint8_t *buf_temp __attribute__((unused)),
+ int fd, int cmd, abi_long arg)
+{
+ struct live_urb *lurb;
+
+ /* map target address back to host URB with metadata. */
+ lurb = urb_hashtable_lookup(arg);
+ if (!lurb) {
+ return -TARGET_EFAULT;
+ }
+ return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
+}
+
+static abi_long
+do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp,
+ int fd, int cmd, abi_long arg)
+{
+ const argtype *arg_type = ie->arg_type;
+ int target_size;
+ abi_long ret;
+ void *argptr;
+ int rw_dir;
+ struct live_urb *lurb;
+
+ /*
+ * each submitted URB needs to map to a unique ID for the
+ * kernel, and that unique ID needs to be a pointer to
+ * host memory. hence, we need to malloc for each URB.
+ * isochronous transfers have a variable length struct.
+ */
+ arg_type++;
+ target_size = thunk_type_size(arg_type, THUNK_TARGET);
+
+ /* construct host copy of urb and metadata */
+ lurb = g_try_malloc0(sizeof(struct live_urb));
+ if (!lurb) {
+ return -TARGET_ENOMEM;
+ }
+
+ argptr = lock_user(VERIFY_READ, arg, target_size, 1);
+ if (!argptr) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+ thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST);
+ unlock_user(argptr, arg, 0);
+
+ lurb->target_urb_adr = arg;
+ lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer;
+
+ /* buffer space used depends on endpoint type so lock the entire buffer */
+ /* control type urbs should check the buffer contents for true direction */
+ rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ;
+ lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr,
+ lurb->host_urb.buffer_length, 1);
+ if (lurb->target_buf_ptr == NULL) {
+ g_free(lurb);
+ return -TARGET_EFAULT;
+ }
+
+ /* update buffer pointer in host copy */
+ lurb->host_urb.buffer = lurb->target_buf_ptr;
+
+ ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb));
+ if (is_error(ret)) {
+ unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0);
+ g_free(lurb);
+ } else {
+ urb_hashtable_insert(lurb);
+ }
+
+ return ret;
+}
+#endif /* CONFIG_USBFS */
+
static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd,
int cmd, abi_long arg)
{
@@ -9347,6 +9527,68 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
return ret;
}
#endif
+#ifdef TARGET_MIPS
+ case TARGET_PR_GET_FP_MODE:
+ {
+ CPUMIPSState *env = ((CPUMIPSState *)cpu_env);
+ ret = 0;
+ if (env->CP0_Status & (1 << CP0St_FR)) {
+ ret |= TARGET_PR_FP_MODE_FR;
+ }
+ if (env->CP0_Config5 & (1 << CP0C5_FRE)) {
+ ret |= TARGET_PR_FP_MODE_FRE;
+ }
+ return ret;
+ }
+ case TARGET_PR_SET_FP_MODE:
+ {
+ CPUMIPSState *env = ((CPUMIPSState *)cpu_env);
+ bool old_fr = env->CP0_Status & (1 << CP0St_FR);
+ bool new_fr = arg2 & TARGET_PR_FP_MODE_FR;
+ bool new_fre = arg2 & TARGET_PR_FP_MODE_FRE;
+
+ if (new_fr && !(env->active_fpu.fcr0 & (1 << FCR0_F64))) {
+ /* FR1 is not supported */
+ return -TARGET_EOPNOTSUPP;
+ }
+ if (!new_fr && (env->active_fpu.fcr0 & (1 << FCR0_F64))
+ && !(env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) {
+ /* cannot set FR=0 */
+ return -TARGET_EOPNOTSUPP;
+ }
+ if (new_fre && !(env->active_fpu.fcr0 & (1 << FCR0_FREP))) {
+ /* Cannot set FRE=1 */
+ return -TARGET_EOPNOTSUPP;
+ }
+
+ int i;
+ fpr_t *fpr = env->active_fpu.fpr;
+ for (i = 0; i < 32 ; i += 2) {
+ if (!old_fr && new_fr) {
+ fpr[i].w[!FP_ENDIAN_IDX] = fpr[i + 1].w[FP_ENDIAN_IDX];
+ } else if (old_fr && !new_fr) {
+ fpr[i + 1].w[FP_ENDIAN_IDX] = fpr[i].w[!FP_ENDIAN_IDX];
+ }
+ }
+
+ if (new_fr) {
+ env->CP0_Status |= (1 << CP0St_FR);
+ env->hflags |= MIPS_HFLAG_F64;
+ } else {
+ env->CP0_Status &= ~(1 << CP0St_FR);
+ }
+ if (new_fre) {
+ env->CP0_Config5 |= (1 << CP0C5_FRE);
+ if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
+ env->hflags |= MIPS_HFLAG_FRE;
+ }
+ } else {
+ env->CP0_Config5 &= ~(1 << CP0C5_FRE);
+ }
+
+ return 0;
+ }
+#endif /* MIPS */
#ifdef TARGET_AARCH64
case TARGET_PR_SVE_SET_VL:
/*
@@ -9356,7 +9598,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
* even though the current architectural maximum is VQ=16.
*/
ret = -TARGET_EINVAL;
- if (arm_feature(cpu_env, ARM_FEATURE_SVE)
+ if (cpu_isar_feature(aa64_sve, arm_env_get_cpu(cpu_env))
&& arg2 >= 0 && arg2 <= 512 * 16 && !(arg2 & 15)) {
CPUARMState *env = cpu_env;
ARMCPU *cpu = arm_env_get_cpu(env);
@@ -9375,9 +9617,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
return ret;
case TARGET_PR_SVE_GET_VL:
ret = -TARGET_EINVAL;
- if (arm_feature(cpu_env, ARM_FEATURE_SVE)) {
- CPUARMState *env = cpu_env;
- ret = ((env->vfp.zcr_el[1] & 0xf) + 1) * 16;
+ {
+ ARMCPU *cpu = arm_env_get_cpu(cpu_env);
+ if (cpu_isar_feature(aa64_sve, cpu)) {
+ ret = ((cpu->env.vfp.zcr_el[1] & 0xf) + 1) * 16;
+ }
}
return ret;
#endif /* AARCH64 */
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 18d434d6dc..99bbce083c 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -863,6 +863,34 @@ struct target_pollfd {
#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap)
+/* usb ioctls */
+#define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0)
+#define TARGET_USBDEVFS_BULK TARGET_IOWRU('U', 2)
+#define TARGET_USBDEVFS_RESETEP TARGET_IORU('U', 3)
+#define TARGET_USBDEVFS_SETINTERFACE TARGET_IORU('U', 4)
+#define TARGET_USBDEVFS_SETCONFIGURATION TARGET_IORU('U', 5)
+#define TARGET_USBDEVFS_GETDRIVER TARGET_IOWU('U', 8)
+#define TARGET_USBDEVFS_SUBMITURB TARGET_IORU('U', 10)
+#define TARGET_USBDEVFS_DISCARDURB TARGET_IO('U', 11)
+#define TARGET_USBDEVFS_REAPURB TARGET_IOWU('U', 12)
+#define TARGET_USBDEVFS_REAPURBNDELAY TARGET_IOWU('U', 13)
+#define TARGET_USBDEVFS_DISCSIGNAL TARGET_IORU('U', 14)
+#define TARGET_USBDEVFS_CLAIMINTERFACE TARGET_IORU('U', 15)
+#define TARGET_USBDEVFS_RELEASEINTERFACE TARGET_IORU('U', 16)
+#define TARGET_USBDEVFS_CONNECTINFO TARGET_IOWU('U', 17)
+#define TARGET_USBDEVFS_IOCTL TARGET_IOWRU('U', 18)
+#define TARGET_USBDEVFS_HUB_PORTINFO TARGET_IORU('U', 19)
+#define TARGET_USBDEVFS_RESET TARGET_IO('U', 20)
+#define TARGET_USBDEVFS_CLEAR_HALT TARGET_IORU('U', 21)
+#define TARGET_USBDEVFS_DISCONNECT TARGET_IO('U', 22)
+#define TARGET_USBDEVFS_CONNECT TARGET_IO('U', 23)
+#define TARGET_USBDEVFS_CLAIM_PORT TARGET_IORU('U', 24)
+#define TARGET_USBDEVFS_RELEASE_PORT TARGET_IORU('U', 25)
+#define TARGET_USBDEVFS_GET_CAPABILITIES TARGET_IORU('U', 26)
+#define TARGET_USBDEVFS_DISCONNECT_CLAIM TARGET_IORU('U', 27)
+#define TARGET_USBDEVFS_DROP_PRIVILEGES TARGET_IOWU('U', 30)
+#define TARGET_USBDEVFS_GET_SPEED TARGET_IO('U', 31)
+
/* cdrom commands */
#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */
#define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */
diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h
index 24631b09be..b98a23b0f1 100644
--- a/linux-user/syscall_types.h
+++ b/linux-user/syscall_types.h
@@ -266,3 +266,71 @@ STRUCT(blkpg_ioctl_arg,
TYPE_INT, /* flags */
TYPE_INT, /* datalen */
TYPE_PTRVOID) /* data */
+
+#if defined(CONFIG_USBFS)
+/* usb device ioctls */
+STRUCT(usbdevfs_ctrltransfer,
+ TYPE_CHAR, /* bRequestType */
+ TYPE_CHAR, /* bRequest */
+ TYPE_SHORT, /* wValue */
+ TYPE_SHORT, /* wIndex */
+ TYPE_SHORT, /* wLength */
+ TYPE_INT, /* timeout */
+ TYPE_PTRVOID) /* data */
+
+STRUCT(usbdevfs_bulktransfer,
+ TYPE_INT, /* ep */
+ TYPE_INT, /* len */
+ TYPE_INT, /* timeout */
+ TYPE_PTRVOID) /* data */
+
+STRUCT(usbdevfs_setinterface,
+ TYPE_INT, /* interface */
+ TYPE_INT) /* altsetting */
+
+STRUCT(usbdevfs_disconnectsignal,
+ TYPE_INT, /* signr */
+ TYPE_PTRVOID) /* context */
+
+STRUCT(usbdevfs_getdriver,
+ TYPE_INT, /* interface */
+ MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */
+
+STRUCT(usbdevfs_connectinfo,
+ TYPE_INT, /* devnum */
+ TYPE_CHAR) /* slow */
+
+STRUCT(usbdevfs_iso_packet_desc,
+ TYPE_INT, /* length */
+ TYPE_INT, /* actual_length */
+ TYPE_INT) /* status */
+
+STRUCT(usbdevfs_urb,
+ TYPE_CHAR, /* type */
+ TYPE_CHAR, /* endpoint */
+ TYPE_INT, /* status */
+ TYPE_INT, /* flags */
+ TYPE_PTRVOID, /* buffer */
+ TYPE_INT, /* buffer_length */
+ TYPE_INT, /* actual_length */
+ TYPE_INT, /* start_frame */
+ TYPE_INT, /* union number_of_packets stream_id */
+ TYPE_INT, /* error_count */
+ TYPE_INT, /* signr */
+ TYPE_PTRVOID, /* usercontext */
+ MK_ARRAY(MK_STRUCT(STRUCT_usbdevfs_iso_packet_desc), 0)) /* desc */
+
+STRUCT(usbdevfs_ioctl,
+ TYPE_INT, /* ifno */
+ TYPE_INT, /* ioctl_code */
+ TYPE_PTRVOID) /* data */
+
+STRUCT(usbdevfs_hub_portinfo,
+ TYPE_CHAR, /* nports */
+ MK_ARRAY(TYPE_CHAR, 127)) /* port */
+
+STRUCT(usbdevfs_disconnect_claim,
+ TYPE_INT, /* interface */
+ TYPE_INT, /* flags */
+ MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */
+#endif /* CONFIG_USBFS */