diff options
author | Richard Henderson | 2019-04-27 00:20:51 +0200 |
---|---|---|
committer | Richard Henderson | 2019-05-19 16:30:03 +0200 |
commit | 21ba856499f9c0ccdc05ed04432df059ae76b337 (patch) | |
tree | 6abdfdcadf3436ae79faf616c47f5f8fdd41d7f7 /linux-user | |
parent | target/alpha: Clean up alpha_cpu_dump_state (diff) | |
download | qemu-21ba856499f9c0ccdc05ed04432df059ae76b337.tar.gz qemu-21ba856499f9c0ccdc05ed04432df059ae76b337.tar.xz qemu-21ba856499f9c0ccdc05ed04432df059ae76b337.zip |
target/alpha: Fix user-only floating-point exceptions
Record the software fp control register, as set by the
osf_setsysinfo syscall. Add those masked exceptions
to fpcr_exc_enable. Do not raise a signal for masked
fp exceptions.
Fixes: https://bugs.launchpad.net/bugs/1701835
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'linux-user')
-rw-r--r-- | linux-user/syscall.c | 106 | ||||
-rw-r--r-- | linux-user/syscall_defs.h | 3 |
2 files changed, 53 insertions, 56 deletions
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f5ff6f5dc8..efa3ec2837 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -10223,18 +10223,11 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, switch (arg1) { case TARGET_GSI_IEEE_FP_CONTROL: { - uint64_t swcr, fpcr = cpu_alpha_load_fpcr (cpu_env); - - /* Copied from linux ieee_fpcr_to_swcr. */ - swcr = (fpcr >> 35) & SWCR_STATUS_MASK; - swcr |= (fpcr >> 36) & SWCR_MAP_DMZ; - swcr |= (~fpcr >> 48) & (SWCR_TRAP_ENABLE_INV - | SWCR_TRAP_ENABLE_DZE - | SWCR_TRAP_ENABLE_OVF); - swcr |= (~fpcr >> 57) & (SWCR_TRAP_ENABLE_UNF - | SWCR_TRAP_ENABLE_INE); - swcr |= (fpcr >> 47) & SWCR_MAP_UMZ; - swcr |= (~fpcr >> 41) & SWCR_TRAP_ENABLE_DNO; + uint64_t fpcr = cpu_alpha_load_fpcr(cpu_env); + uint64_t swcr = ((CPUAlphaState *)cpu_env)->swcr; + + swcr &= ~SWCR_STATUS_MASK; + swcr |= (fpcr >> 35) & SWCR_STATUS_MASK; if (put_user_u64 (swcr, arg2)) return -TARGET_EFAULT; @@ -10261,25 +10254,24 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, switch (arg1) { case TARGET_SSI_IEEE_FP_CONTROL: { - uint64_t swcr, fpcr, orig_fpcr; + uint64_t swcr, fpcr; if (get_user_u64 (swcr, arg2)) { return -TARGET_EFAULT; } - orig_fpcr = cpu_alpha_load_fpcr(cpu_env); - fpcr = orig_fpcr & FPCR_DYN_MASK; - - /* Copied from linux ieee_swcr_to_fpcr. */ - fpcr |= (swcr & SWCR_STATUS_MASK) << 35; - fpcr |= (swcr & SWCR_MAP_DMZ) << 36; - fpcr |= (~swcr & (SWCR_TRAP_ENABLE_INV - | SWCR_TRAP_ENABLE_DZE - | SWCR_TRAP_ENABLE_OVF)) << 48; - fpcr |= (~swcr & (SWCR_TRAP_ENABLE_UNF - | SWCR_TRAP_ENABLE_INE)) << 57; - fpcr |= (swcr & SWCR_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0); - fpcr |= (~swcr & SWCR_TRAP_ENABLE_DNO) << 41; + /* + * The kernel calls swcr_update_status to update the + * status bits from the fpcr at every point that it + * could be queried. Therefore, we store the status + * bits only in FPCR. + */ + ((CPUAlphaState *)cpu_env)->swcr + = swcr & (SWCR_TRAP_ENABLE_MASK | SWCR_MAP_MASK); + + fpcr = cpu_alpha_load_fpcr(cpu_env); + fpcr &= ((uint64_t)FPCR_DYN_MASK << 32); + fpcr |= alpha_ieee_swcr_to_fpcr(swcr); cpu_alpha_store_fpcr(cpu_env, fpcr); ret = 0; } @@ -10287,44 +10279,47 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, case TARGET_SSI_IEEE_RAISE_EXCEPTION: { - uint64_t exc, fpcr, orig_fpcr; - int si_code; + uint64_t exc, fpcr, fex; if (get_user_u64(exc, arg2)) { return -TARGET_EFAULT; } + exc &= SWCR_STATUS_MASK; + fpcr = cpu_alpha_load_fpcr(cpu_env); - orig_fpcr = cpu_alpha_load_fpcr(cpu_env); - - /* We only add to the exception status here. */ - fpcr = orig_fpcr | ((exc & SWCR_STATUS_MASK) << 35); + /* Old exceptions are not signaled. */ + fex = alpha_ieee_fpcr_to_swcr(fpcr); + fex = exc & ~fex; + fex >>= SWCR_STATUS_TO_EXCSUM_SHIFT; + fex &= ((CPUArchState *)cpu_env)->swcr; + /* Update the hardware fpcr. */ + fpcr |= alpha_ieee_swcr_to_fpcr(exc); cpu_alpha_store_fpcr(cpu_env, fpcr); - ret = 0; - /* Old exceptions are not signaled. */ - fpcr &= ~(orig_fpcr & FPCR_STATUS_MASK); - - /* If any exceptions set by this call, - and are unmasked, send a signal. */ - si_code = 0; - if ((fpcr & (FPCR_INE | FPCR_INED)) == FPCR_INE) { - si_code = TARGET_FPE_FLTRES; - } - if ((fpcr & (FPCR_UNF | FPCR_UNFD)) == FPCR_UNF) { - si_code = TARGET_FPE_FLTUND; - } - if ((fpcr & (FPCR_OVF | FPCR_OVFD)) == FPCR_OVF) { - si_code = TARGET_FPE_FLTOVF; - } - if ((fpcr & (FPCR_DZE | FPCR_DZED)) == FPCR_DZE) { - si_code = TARGET_FPE_FLTDIV; - } - if ((fpcr & (FPCR_INV | FPCR_INVD)) == FPCR_INV) { - si_code = TARGET_FPE_FLTINV; - } - if (si_code != 0) { + if (fex) { + int si_code = TARGET_FPE_FLTUNK; target_siginfo_t info; + + if (fex & SWCR_TRAP_ENABLE_DNO) { + si_code = TARGET_FPE_FLTUND; + } + if (fex & SWCR_TRAP_ENABLE_INE) { + si_code = TARGET_FPE_FLTRES; + } + if (fex & SWCR_TRAP_ENABLE_UNF) { + si_code = TARGET_FPE_FLTUND; + } + if (fex & SWCR_TRAP_ENABLE_OVF) { + si_code = TARGET_FPE_FLTOVF; + } + if (fex & SWCR_TRAP_ENABLE_DZE) { + si_code = TARGET_FPE_FLTDIV; + } + if (fex & SWCR_TRAP_ENABLE_INV) { + si_code = TARGET_FPE_FLTINV; + } + info.si_signo = SIGFPE; info.si_errno = 0; info.si_code = si_code; @@ -10333,6 +10328,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, queue_signal((CPUArchState *)cpu_env, info.si_signo, QEMU_SI_FAULT, &info); } + ret = 0; } break; diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 12c8407144..1f5b2d18db 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -635,7 +635,8 @@ typedef struct target_siginfo { #define TARGET_FPE_FLTRES (6) /* floating point inexact result */ #define TARGET_FPE_FLTINV (7) /* floating point invalid operation */ #define TARGET_FPE_FLTSUB (8) /* subscript out of range */ -#define TARGET_NSIGFPE 8 +#define TARGET_FPE_FLTUNK (14) /* undiagnosed fp exception */ +#define TARGET_NSIGFPE 15 /* * SIGSEGV si_codes |