mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-15 06:01:58 -06:00
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:
parent
ab546bd238
commit
3986a1721e
1 changed files with 77 additions and 66 deletions
|
@ -299,85 +299,96 @@ 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) {
|
}
|
||||||
switch (n) {
|
|
||||||
case ARM_NR_cacheflush:
|
if (n > ARM_NR_BASE) {
|
||||||
/* nop */
|
switch (n) {
|
||||||
break;
|
case ARM_NR_cacheflush:
|
||||||
case ARM_NR_set_tls:
|
/* nop */
|
||||||
cpu_set_tls(env, env->regs[0]);
|
break;
|
||||||
env->regs[0] = 0;
|
case ARM_NR_set_tls:
|
||||||
break;
|
cpu_set_tls(env, env->regs[0]);
|
||||||
case ARM_NR_breakpoint:
|
env->regs[0] = 0;
|
||||||
env->regs[15] -= env->thumb ? 2 : 4;
|
break;
|
||||||
goto excp_debug;
|
case ARM_NR_breakpoint:
|
||||||
case ARM_NR_get_tls:
|
env->regs[15] -= env->thumb ? 2 : 4;
|
||||||
env->regs[0] = cpu_get_tls(env);
|
goto excp_debug;
|
||||||
break;
|
case ARM_NR_get_tls:
|
||||||
default:
|
env->regs[0] = cpu_get_tls(env);
|
||||||
if (n < 0xf0800) {
|
break;
|
||||||
/*
|
default:
|
||||||
* Syscalls 0xf0000..0xf07ff (or 0x9f0000..
|
if (n < 0xf0800) {
|
||||||
* 0x9f07ff in OABI numbering) are defined
|
/*
|
||||||
* to return -ENOSYS rather than raising
|
* Syscalls 0xf0000..0xf07ff (or 0x9f0000..
|
||||||
* SIGILL. Note that we have already
|
* 0x9f07ff in OABI numbering) are defined
|
||||||
* removed the 0x900000 prefix.
|
* to return -ENOSYS rather than raising
|
||||||
*/
|
* SIGILL. Note that we have already
|
||||||
qemu_log_mask(LOG_UNIMP,
|
* removed the 0x900000 prefix.
|
||||||
"qemu: Unsupported ARM syscall: 0x%x\n",
|
*/
|
||||||
n);
|
qemu_log_mask(LOG_UNIMP,
|
||||||
env->regs[0] = -TARGET_ENOSYS;
|
"qemu: Unsupported ARM syscall: 0x%x\n",
|
||||||
|
n);
|
||||||
|
env->regs[0] = -TARGET_ENOSYS;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Otherwise SIGILL. This includes any SWI with
|
||||||
|
* immediate not originally 0x9fxxxx, because
|
||||||
|
* of the earlier XOR.
|
||||||
|
*/
|
||||||
|
info.si_signo = TARGET_SIGILL;
|
||||||
|
info.si_errno = 0;
|
||||||
|
info.si_code = TARGET_ILL_ILLTRP;
|
||||||
|
info._sifields._sigfault._addr = env->regs[15];
|
||||||
|
if (env->thumb) {
|
||||||
|
info._sifields._sigfault._addr -= 2;
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise SIGILL */
|
info._sifields._sigfault._addr -= 4;
|
||||||
info.si_signo = TARGET_SIGILL;
|
|
||||||
info.si_errno = 0;
|
|
||||||
info.si_code = TARGET_ILL_ILLTRP;
|
|
||||||
info._sifields._sigfault._addr = env->regs[15];
|
|
||||||
if (env->thumb) {
|
|
||||||
info._sifields._sigfault._addr -= 2;
|
|
||||||
} else {
|
|
||||||
info._sifields._sigfault._addr -= 4;
|
|
||||||
}
|
|
||||||
queue_signal(env, info.si_signo,
|
|
||||||
QEMU_SI_FAULT, &info);
|
|
||||||
}
|
}
|
||||||
break;
|
queue_signal(env, info.si_signo,
|
||||||
}
|
QEMU_SI_FAULT, &info);
|
||||||
} else {
|
|
||||||
ret = do_syscall(env,
|
|
||||||
n,
|
|
||||||
env->regs[0],
|
|
||||||
env->regs[1],
|
|
||||||
env->regs[2],
|
|
||||||
env->regs[3],
|
|
||||||
env->regs[4],
|
|
||||||
env->regs[5],
|
|
||||||
0, 0);
|
|
||||||
if (ret == -TARGET_ERESTARTSYS) {
|
|
||||||
env->regs[15] -= env->thumb ? 2 : 4;
|
|
||||||
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
|
|
||||||
env->regs[0] = ret;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
goto error;
|
ret = do_syscall(env,
|
||||||
|
n,
|
||||||
|
env->regs[0],
|
||||||
|
env->regs[1],
|
||||||
|
env->regs[2],
|
||||||
|
env->regs[3],
|
||||||
|
env->regs[4],
|
||||||
|
env->regs[5],
|
||||||
|
0, 0);
|
||||||
|
if (ret == -TARGET_ERESTARTSYS) {
|
||||||
|
env->regs[15] -= env->thumb ? 2 : 4;
|
||||||
|
} else if (ret != -TARGET_QEMU_ESIGRETURN) {
|
||||||
|
env->regs[0] = ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue