linux-user/arm: Fix identification of syscall numbers

Our code to identify syscall numbers has some issues:
 * for Thumb mode, we never need the immediate value from the insn,
   but we always read it anyway
 * bad immediate values in the svc insn should cause a SIGILL, but we
   were abort()ing instead (via "goto error")

We can fix both these things by refactoring the code that identifies
the syscall number to more closely follow the kernel COMPAT_OABI code:
 * for Thumb it is always r7
 * for Arm, if the immediate value is 0, then this is an EABI call
   with the syscall number in r7
 * otherwise, we XOR the immediate value with 0x900000
   (ARM_SYSCALL_BASE for QEMU; __NR_OABI_SYSCALL_BASE in the kernel),
   which converts valid syscall immediates into the desired value,
   and puts all invalid immediates in the range 0x100000 or above
 * then we can just let the existing "value too large, deliver
   SIGILL" case handle invalid numbers, and drop the 'goto error'

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
Message-id: 20200420212206.12776-5-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2020-04-20 22:22:06 +01:00
parent ab546bd238
commit 3986a1721e

View file

@ -299,24 +299,34 @@ void cpu_loop(CPUARMState *env)
env->eabi = 1; env->eabi = 1;
/* system call */ /* system call */
if (env->thumb) { if (env->thumb) {
/* FIXME - what to do if get_user() fails? */ /* Thumb is always EABI style with syscall number in r7 */
get_user_code_u16(insn, env->regs[15] - 2, env); n = env->regs[7];
n = insn & 0xff;
} else { } else {
/*
* Equivalent of kernel CONFIG_OABI_COMPAT: read the
* Arm SVC insn to extract the immediate, which is the
* syscall number in OABI.
*/
/* FIXME - what to do if get_user() fails? */ /* FIXME - what to do if get_user() fails? */
get_user_code_u32(insn, env->regs[15] - 4, env); get_user_code_u32(insn, env->regs[15] - 4, env);
n = insn & 0xffffff; n = insn & 0xffffff;
} if (n == 0) {
/* zero immediate: EABI, syscall number in r7 */
if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) {
/* linux syscall */
if (env->thumb || n == 0) {
n = env->regs[7]; n = env->regs[7];
} else { } else {
n -= ARM_SYSCALL_BASE; /*
* This XOR matches the kernel code: an immediate
* in the valid range (0x900000 .. 0x9fffff) is
* converted into the correct EABI-style syscall
* number; invalid immediates end up as values
* > 0xfffff and are handled below as out-of-range.
*/
n ^= ARM_SYSCALL_BASE;
env->eabi = 0; env->eabi = 0;
} }
if ( n > ARM_NR_BASE) { }
if (n > ARM_NR_BASE) {
switch (n) { switch (n) {
case ARM_NR_cacheflush: case ARM_NR_cacheflush:
/* nop */ /* nop */
@ -345,7 +355,11 @@ void cpu_loop(CPUARMState *env)
n); n);
env->regs[0] = -TARGET_ENOSYS; env->regs[0] = -TARGET_ENOSYS;
} else { } else {
/* Otherwise SIGILL */ /*
* Otherwise SIGILL. This includes any SWI with
* immediate not originally 0x9fxxxx, because
* of the earlier XOR.
*/
info.si_signo = TARGET_SIGILL; info.si_signo = TARGET_SIGILL;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TARGET_ILL_ILLTRP; info.si_code = TARGET_ILL_ILLTRP;
@ -376,9 +390,6 @@ void cpu_loop(CPUARMState *env)
env->regs[0] = ret; env->regs[0] = ret;
} }
} }
} else {
goto error;
}
} }
break; break;
case EXCP_SEMIHOST: case EXCP_SEMIHOST: