diff options
Diffstat (limited to 'linux-user/host')
35 files changed, 1391 insertions, 0 deletions
diff --git a/linux-user/host/aarch64/host-signal.h b/linux-user/host/aarch64/host-signal.h new file mode 100644 index 000000000..9770b36dc --- /dev/null +++ b/linux-user/host/aarch64/host-signal.h @@ -0,0 +1,79 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef AARCH64_HOST_SIGNAL_H +#define AARCH64_HOST_SIGNAL_H + +/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ +#ifndef ESR_MAGIC +#define ESR_MAGIC 0x45535201 +struct esr_context { + struct _aarch64_ctx head; + uint64_t esr; +}; +#endif + +static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) +{ + return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; +} + +static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) +{ + return (struct _aarch64_ctx *)((char *)hdr + hdr->size); +} + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.pc; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.pc = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + struct _aarch64_ctx *hdr; + uint32_t insn; + + /* Find the esr_context, which has the WnR bit in it */ + for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { + if (hdr->magic == ESR_MAGIC) { + struct esr_context const *ec = (struct esr_context const *)hdr; + uint64_t esr = ec->esr; + + /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ + return extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; + } + } + + /* + * Fall back to parsing instructions; will only be needed + * for really ancient (pre-3.16) kernels. + */ + insn = *(uint32_t *)host_signal_pc(uc); + + return (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */ + || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */ + || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */ + || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */ + || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */ + || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */ + || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */ + /* Ignore bits 10, 11 & 21, controlling indexing. */ + || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */ + || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */ + /* Ignore bits 23 & 24, controlling indexing. */ + || (insn & 0x3a400000) == 0x28000000; /* C3.3.7,14-16 */ +} + +#endif diff --git a/linux-user/host/aarch64/hostdep.h b/linux-user/host/aarch64/hostdep.h new file mode 100644 index 000000000..39299d798 --- /dev/null +++ b/linux-user/host/aarch64/hostdep.h @@ -0,0 +1,18 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef AARCH64_HOSTDEP_H +#define AARCH64_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/aarch64/safe-syscall.inc.S b/linux-user/host/aarch64/safe-syscall.inc.S new file mode 100644 index 000000000..bc1f5a979 --- /dev/null +++ b/linux-user/host/aarch64/safe-syscall.inc.S @@ -0,0 +1,75 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson <rth@twiddle.net> + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, #function + .type safe_syscall_start, #function + .type safe_syscall_end, #function + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + /* The syscall calling convention isn't the same as the + * C one: + * we enter with x0 == *signal_pending + * x1 == syscall number + * x2 ... x7, (stack) == syscall arguments + * and return the result in x0 + * and the syscall instruction needs + * x8 == syscall number + * x0 ... x6 == syscall arguments + * and returns the result in x0 + * Shuffle everything around appropriately. + */ + mov x9, x0 /* signal_pending pointer */ + mov x8, x1 /* syscall number */ + mov x0, x2 /* syscall arguments */ + mov x1, x3 + mov x2, x4 + mov x3, x5 + mov x4, x6 + mov x5, x7 + ldr x6, [sp] + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + ldr w10, [x9] + cbnz w10, 0f + svc 0x0 +safe_syscall_end: + /* code path for having successfully executed the syscall */ + ret + +0: + /* code path when we didn't execute the syscall */ + mov x0, #-TARGET_ERESTARTSYS + ret + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/host/alpha/host-signal.h b/linux-user/host/alpha/host-signal.h new file mode 100644 index 000000000..f4c942948 --- /dev/null +++ b/linux-user/host/alpha/host-signal.h @@ -0,0 +1,47 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_HOST_SIGNAL_H +#define ALPHA_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.sc_pc; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.sc_pc = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t *pc = (uint32_t *)host_signal_pc(uc); + uint32_t insn = *pc; + + /* XXX: need kernel patch to get write flag faster */ + switch (insn >> 26) { + case 0x0d: /* stw */ + case 0x0e: /* stb */ + case 0x0f: /* stq_u */ + case 0x24: /* stf */ + case 0x25: /* stg */ + case 0x26: /* sts */ + case 0x27: /* stt */ + case 0x2c: /* stl */ + case 0x2d: /* stq */ + case 0x2e: /* stl_c */ + case 0x2f: /* stq_c */ + return true; + } + return false; +} + +#endif diff --git a/linux-user/host/arm/host-signal.h b/linux-user/host/arm/host-signal.h new file mode 100644 index 000000000..6c095773c --- /dev/null +++ b/linux-user/host/arm/host-signal.h @@ -0,0 +1,35 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ARM_HOST_SIGNAL_H +#define ARM_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.arm_pc; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.arm_pc = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * In the FSR, bit 11 is WnR, assuming a v6 or + * later processor. On v5 we will always report + * this as a read, which will fail later. + */ + uint32_t fsr = uc->uc_mcontext.error_code; + return extract32(fsr, 11, 1); +} + +#endif diff --git a/linux-user/host/arm/hostdep.h b/linux-user/host/arm/hostdep.h new file mode 100644 index 000000000..86b137875 --- /dev/null +++ b/linux-user/host/arm/hostdep.h @@ -0,0 +1,18 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef ARM_HOSTDEP_H +#define ARM_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/arm/safe-syscall.inc.S b/linux-user/host/arm/safe-syscall.inc.S new file mode 100644 index 000000000..88c495850 --- /dev/null +++ b/linux-user/host/arm/safe-syscall.inc.S @@ -0,0 +1,90 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson <rth@twiddle.net> + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, %function + + .cfi_sections .debug_frame + + .text + .syntax unified + .arm + .align 2 + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .fnstart + .cfi_startproc + mov r12, sp /* save entry stack */ + push { r4, r5, r6, r7, r8, lr } + .save { r4, r5, r6, r7, r8, lr } + .cfi_adjust_cfa_offset 24 + .cfi_rel_offset r4, 0 + .cfi_rel_offset r5, 4 + .cfi_rel_offset r6, 8 + .cfi_rel_offset r7, 12 + .cfi_rel_offset r8, 16 + .cfi_rel_offset lr, 20 + + /* The syscall calling convention isn't the same as the C one: + * we enter with r0 == *signal_pending + * r1 == syscall number + * r2, r3, [sp+0] ... [sp+12] == syscall arguments + * and return the result in r0 + * and the syscall instruction needs + * r7 == syscall number + * r0 ... r6 == syscall arguments + * and returns the result in r0 + * Shuffle everything around appropriately. + * Note the 16 bytes that we pushed to save registers. + */ + mov r8, r0 /* copy signal_pending */ + mov r7, r1 /* syscall number */ + mov r0, r2 /* syscall args */ + mov r1, r3 + ldm r12, { r2, r3, r4, r5, r6 } + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + ldr r12, [r8] /* signal_pending */ + tst r12, r12 + bne 1f + swi 0 +safe_syscall_end: + /* code path for having successfully executed the syscall */ + pop { r4, r5, r6, r7, r8, pc } + +1: + /* code path when we didn't execute the syscall */ + ldr r0, =-TARGET_ERESTARTSYS + pop { r4, r5, r6, r7, r8, pc } + .fnend + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/host/i386/host-signal.h b/linux-user/host/i386/host-signal.h new file mode 100644 index 000000000..abe1ece5c --- /dev/null +++ b/linux-user/host/i386/host-signal.h @@ -0,0 +1,30 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef I386_HOST_SIGNAL_H +#define I386_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_EIP]; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.gregs[REG_EIP] = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe + && (uc->uc_mcontext.gregs[REG_ERR] & 0x2); +} + +#endif diff --git a/linux-user/host/i386/hostdep.h b/linux-user/host/i386/hostdep.h new file mode 100644 index 000000000..ce7136501 --- /dev/null +++ b/linux-user/host/i386/hostdep.h @@ -0,0 +1,18 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef I386_HOSTDEP_H +#define I386_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/i386/safe-syscall.inc.S b/linux-user/host/i386/safe-syscall.inc.S new file mode 100644 index 000000000..9e58fc650 --- /dev/null +++ b/linux-user/host/i386/safe-syscall.inc.S @@ -0,0 +1,100 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson <rth@twiddle.net> + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + push %ebp + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebp, 0 + push %esi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset esi, 0 + push %edi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edi, 0 + push %ebx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ebx, 0 + + /* The syscall calling convention isn't the same as the C one: + * we enter with 0(%esp) == return address + * 4(%esp) == *signal_pending + * 8(%esp) == syscall number + * 12(%esp) ... 32(%esp) == syscall arguments + * and return the result in eax + * and the syscall instruction needs + * eax == syscall number + * ebx, ecx, edx, esi, edi, ebp == syscall arguments + * and returns the result in eax + * Shuffle everything around appropriately. + * Note the 16 bytes that we pushed to save registers. + */ + mov 12+16(%esp), %ebx /* the syscall arguments */ + mov 16+16(%esp), %ecx + mov 20+16(%esp), %edx + mov 24+16(%esp), %esi + mov 28+16(%esp), %edi + mov 32+16(%esp), %ebp + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + mov 4+16(%esp), %eax /* signal_pending */ + cmpl $0, (%eax) + jnz 1f + mov 8+16(%esp), %eax /* syscall number */ + int $0x80 +safe_syscall_end: + /* code path for having successfully executed the syscall */ + pop %ebx + .cfi_remember_state + .cfi_adjust_cfa_offset -4 + .cfi_restore ebx + pop %edi + .cfi_adjust_cfa_offset -4 + .cfi_restore edi + pop %esi + .cfi_adjust_cfa_offset -4 + .cfi_restore esi + pop %ebp + .cfi_adjust_cfa_offset -4 + .cfi_restore ebp + ret + +1: + /* code path when we didn't execute the syscall */ + .cfi_restore_state + mov $-TARGET_ERESTARTSYS, %eax + jmp safe_syscall_end + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/host/ia64/hostdep.h b/linux-user/host/ia64/hostdep.h new file mode 100644 index 000000000..263bf7658 --- /dev/null +++ b/linux-user/host/ia64/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef IA64_HOSTDEP_H +#define IA64_HOSTDEP_H + +#endif diff --git a/linux-user/host/mips/host-signal.h b/linux-user/host/mips/host-signal.h new file mode 100644 index 000000000..c666ed8c3 --- /dev/null +++ b/linux-user/host/mips/host-signal.h @@ -0,0 +1,67 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_HOST_SIGNAL_H +#define MIPS_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.pc; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.pc = pc; +} + +#if defined(__misp16) || defined(__mips_micromips) +#error "Unsupported encoding" +#endif + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t insn = *(uint32_t *)host_signal_pc(uc); + + /* Detect all store instructions at program counter. */ + switch ((insn >> 26) & 077) { + case 050: /* SB */ + case 051: /* SH */ + case 052: /* SWL */ + case 053: /* SW */ + case 054: /* SDL */ + case 055: /* SDR */ + case 056: /* SWR */ + case 070: /* SC */ + case 071: /* SWC1 */ + case 074: /* SCD */ + case 075: /* SDC1 */ + case 077: /* SD */ +#if !defined(__mips_isa_rev) || __mips_isa_rev < 6 + case 072: /* SWC2 */ + case 076: /* SDC2 */ +#endif + return true; + case 023: /* COP1X */ + /* + * Required in all versions of MIPS64 since + * MIPS64r1 and subsequent versions of MIPS32r2. + */ + switch (insn & 077) { + case 010: /* SWXC1 */ + case 011: /* SDXC1 */ + case 015: /* SUXC1 */ + return true; + } + break; + } + return false; +} + +#endif diff --git a/linux-user/host/mips/hostdep.h b/linux-user/host/mips/hostdep.h new file mode 100644 index 000000000..ba111d75c --- /dev/null +++ b/linux-user/host/mips/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_HOSTDEP_H +#define MIPS_HOSTDEP_H + +#endif diff --git a/linux-user/host/ppc/host-signal.h b/linux-user/host/ppc/host-signal.h new file mode 100644 index 000000000..1d8e658ff --- /dev/null +++ b/linux-user/host/ppc/host-signal.h @@ -0,0 +1,30 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef PPC_HOST_SIGNAL_H +#define PPC_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.regs->nip; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.regs->nip = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + return uc->uc_mcontext.regs->trap != 0x400 + && (uc->uc_mcontext.regs->dsisr & 0x02000000); +} + +#endif diff --git a/linux-user/host/ppc/hostdep.h b/linux-user/host/ppc/hostdep.h new file mode 100644 index 000000000..23d8bd9d4 --- /dev/null +++ b/linux-user/host/ppc/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef PPC_HOSTDEP_H +#define PPC_HOSTDEP_H + +#endif diff --git a/linux-user/host/ppc64/host-signal.h b/linux-user/host/ppc64/host-signal.h new file mode 100644 index 000000000..a353c22a9 --- /dev/null +++ b/linux-user/host/ppc64/host-signal.h @@ -0,0 +1 @@ +#include "../ppc/host-signal.h" diff --git a/linux-user/host/ppc64/hostdep.h b/linux-user/host/ppc64/hostdep.h new file mode 100644 index 000000000..0c290dd90 --- /dev/null +++ b/linux-user/host/ppc64/hostdep.h @@ -0,0 +1,18 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef PPC64_HOSTDEP_H +#define PPC64_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/ppc64/safe-syscall.inc.S b/linux-user/host/ppc64/safe-syscall.inc.S new file mode 100644 index 000000000..875133173 --- /dev/null +++ b/linux-user/host/ppc64/safe-syscall.inc.S @@ -0,0 +1,96 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson <rth@twiddle.net> + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + .text + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +#if _CALL_ELF == 2 +safe_syscall_base: + .cfi_startproc + .localentry safe_syscall_base,0 +#else + .section ".opd","aw" + .align 3 +safe_syscall_base: + .quad .L.safe_syscall_base,.TOC.@tocbase,0 + .previous +.L.safe_syscall_base: + .cfi_startproc +#endif + /* We enter with r3 == *signal_pending + * r4 == syscall number + * r5 ... r10 == syscall arguments + * and return the result in r3 + * and the syscall instruction needs + * r0 == syscall number + * r3 ... r8 == syscall arguments + * and returns the result in r3 + * Shuffle everything around appropriately. + */ + std 14, 16(1) /* Preserve r14 in SP+16 */ + .cfi_offset 14, 16 + mr 14, 3 /* signal_pending */ + mr 0, 4 /* syscall number */ + mr 3, 5 /* syscall arguments */ + mr 4, 6 + mr 5, 7 + mr 6, 8 + mr 7, 9 + mr 8, 10 + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + lwz 12, 0(14) + cmpwi 0, 12, 0 + bne- 0f + sc +safe_syscall_end: + /* code path when we did execute the syscall */ + ld 14, 16(1) /* restore r14 to its original value */ + bnslr+ + + /* syscall failed; return negative errno */ + neg 3, 3 + blr + + /* code path when we didn't execute the syscall */ +0: addi 3, 0, -TARGET_ERESTARTSYS + ld 14, 16(1) /* restore r14 to its original value */ + blr + .cfi_endproc + +#if _CALL_ELF == 2 + .size safe_syscall_base, .-safe_syscall_base +#else + .size safe_syscall_base, .-.L.safe_syscall_base + .size .L.safe_syscall_base, .-.L.safe_syscall_base +#endif diff --git a/linux-user/host/riscv/host-signal.h b/linux-user/host/riscv/host-signal.h new file mode 100644 index 000000000..a4f170efb --- /dev/null +++ b/linux-user/host/riscv/host-signal.h @@ -0,0 +1,63 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef RISCV_HOST_SIGNAL_H +#define RISCV_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.__gregs[REG_PC]; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.__gregs[REG_PC] = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + /* + * Detect store by reading the instruction at the program counter. + * Do not read more than 16 bits, because we have not yet determined + * the size of the instruction. + */ + const uint16_t *pinsn = (const uint16_t *)host_signal_pc(uc); + uint16_t insn = pinsn[0]; + + /* 16-bit instructions */ + switch (insn & 0xe003) { + case 0xa000: /* c.fsd */ + case 0xc000: /* c.sw */ + case 0xe000: /* c.sd (rv64) / c.fsw (rv32) */ + case 0xa002: /* c.fsdsp */ + case 0xc002: /* c.swsp */ + case 0xe002: /* c.sdsp (rv64) / c.fswsp (rv32) */ + return true; + } + + /* 32-bit instructions, major opcodes */ + switch (insn & 0x7f) { + case 0x23: /* store */ + case 0x27: /* store-fp */ + return true; + case 0x2f: /* amo */ + /* + * The AMO function code is in bits 25-31, unread as yet. + * The AMO functions are LR (read), SC (write), and the + * rest are all read-modify-write. + */ + insn = pinsn[1]; + return (insn >> 11) != 2; /* LR */ + } + + return false; +} + +#endif diff --git a/linux-user/host/riscv/hostdep.h b/linux-user/host/riscv/hostdep.h new file mode 100644 index 000000000..7f67c2286 --- /dev/null +++ b/linux-user/host/riscv/hostdep.h @@ -0,0 +1,14 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef RISCV_HOSTDEP_H +#define RISCV_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/riscv/safe-syscall.inc.S b/linux-user/host/riscv/safe-syscall.inc.S new file mode 100644 index 000000000..9ca3fbfd1 --- /dev/null +++ b/linux-user/host/riscv/safe-syscall.inc.S @@ -0,0 +1,77 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson <rth@twiddle.net> + * Copyright (C) 2018 Linaro, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + .type safe_syscall_start, @function + .type safe_syscall_end, @function + + /* + * This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + /* + * The syscall calling convention is nearly the same as C: + * we enter with a0 == *signal_pending + * a1 == syscall number + * a2 ... a7 == syscall arguments + * and return the result in a0 + * and the syscall instruction needs + * a7 == syscall number + * a0 ... a5 == syscall arguments + * and returns the result in a0 + * Shuffle everything around appropriately. + */ + mv t0, a0 /* signal_pending pointer */ + mv t1, a1 /* syscall number */ + mv a0, a2 /* syscall arguments */ + mv a1, a3 + mv a2, a4 + mv a3, a5 + mv a4, a6 + mv a5, a7 + mv a7, t1 + + /* + * This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* If signal_pending is non-zero, don't do the call */ + lw t1, 0(t0) + bnez t1, 0f + scall +safe_syscall_end: + /* code path for having successfully executed the syscall */ + ret + +0: + /* code path when we didn't execute the syscall */ + li a0, -TARGET_ERESTARTSYS + ret + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/host/s390/host-signal.h b/linux-user/host/s390/host-signal.h new file mode 100644 index 000000000..a524f2ab0 --- /dev/null +++ b/linux-user/host/s390/host-signal.h @@ -0,0 +1,98 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef S390_HOST_SIGNAL_H +#define S390_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.psw.addr; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.psw.addr = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint16_t *pinsn = (uint16_t *)host_signal_pc(uc); + + /* + * ??? On linux, the non-rt signal handler has 4 (!) arguments instead + * of the normal 2 arguments. The 4th argument contains the "Translation- + * Exception Identification for DAT Exceptions" from the hardware (aka + * "int_parm_long"), which does in fact contain the is_write value. + * The rt signal handler, as far as I can tell, does not give this value + * at all. Not that we could get to it from here even if it were. + * So fall back to parsing instructions. Treat read-modify-write ones as + * writes, which is not fully correct, but for tracking self-modifying code + * this is better than treating them as reads. Checking si_addr page flags + * might be a viable improvement, albeit a racy one. + */ + /* ??? This is not even close to complete. */ + switch (pinsn[0] >> 8) { + case 0x50: /* ST */ + case 0x42: /* STC */ + case 0x40: /* STH */ + case 0xba: /* CS */ + case 0xbb: /* CDS */ + return true; + case 0xc4: /* RIL format insns */ + switch (pinsn[0] & 0xf) { + case 0xf: /* STRL */ + case 0xb: /* STGRL */ + case 0x7: /* STHRL */ + return true; + } + break; + case 0xc8: /* SSF format insns */ + switch (pinsn[0] & 0xf) { + case 0x2: /* CSST */ + return true; + } + break; + case 0xe3: /* RXY format insns */ + switch (pinsn[2] & 0xff) { + case 0x50: /* STY */ + case 0x24: /* STG */ + case 0x72: /* STCY */ + case 0x70: /* STHY */ + case 0x8e: /* STPQ */ + case 0x3f: /* STRVH */ + case 0x3e: /* STRV */ + case 0x2f: /* STRVG */ + return true; + } + break; + case 0xeb: /* RSY format insns */ + switch (pinsn[2] & 0xff) { + case 0x14: /* CSY */ + case 0x30: /* CSG */ + case 0x31: /* CDSY */ + case 0x3e: /* CDSG */ + case 0xe4: /* LANG */ + case 0xe6: /* LAOG */ + case 0xe7: /* LAXG */ + case 0xe8: /* LAAG */ + case 0xea: /* LAALG */ + case 0xf4: /* LAN */ + case 0xf6: /* LAO */ + case 0xf7: /* LAX */ + case 0xfa: /* LAAL */ + case 0xf8: /* LAA */ + return true; + } + break; + } + return false; +} + +#endif diff --git a/linux-user/host/s390/hostdep.h b/linux-user/host/s390/hostdep.h new file mode 100644 index 000000000..afcba5a16 --- /dev/null +++ b/linux-user/host/s390/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef S390_HOSTDEP_H +#define S390_HOSTDEP_H + +#endif diff --git a/linux-user/host/s390x/host-signal.h b/linux-user/host/s390x/host-signal.h new file mode 100644 index 000000000..0e83f9358 --- /dev/null +++ b/linux-user/host/s390x/host-signal.h @@ -0,0 +1 @@ +#include "../s390/host-signal.h" diff --git a/linux-user/host/s390x/hostdep.h b/linux-user/host/s390x/hostdep.h new file mode 100644 index 000000000..d80114585 --- /dev/null +++ b/linux-user/host/s390x/hostdep.h @@ -0,0 +1,18 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef S390X_HOSTDEP_H +#define S390X_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/s390x/safe-syscall.inc.S b/linux-user/host/s390x/safe-syscall.inc.S new file mode 100644 index 000000000..414b44ad3 --- /dev/null +++ b/linux-user/host/s390x/safe-syscall.inc.S @@ -0,0 +1,90 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson <rth@twiddle.net> + * Copyright (C) 2016 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + stmg %r6,%r15,48(%r15) /* save all call-saved registers */ + .cfi_offset %r15,-40 + .cfi_offset %r14,-48 + .cfi_offset %r13,-56 + .cfi_offset %r12,-64 + .cfi_offset %r11,-72 + .cfi_offset %r10,-80 + .cfi_offset %r9,-88 + .cfi_offset %r8,-96 + .cfi_offset %r7,-104 + .cfi_offset %r6,-112 + lgr %r1,%r15 + lg %r0,8(%r15) /* load eos */ + aghi %r15,-160 + .cfi_adjust_cfa_offset 160 + stg %r1,0(%r15) /* store back chain */ + stg %r0,8(%r15) /* store eos */ + + /* The syscall calling convention isn't the same as the + * C one: + * we enter with r2 == *signal_pending + * r3 == syscall number + * r4, r5, r6, (stack) == syscall arguments + * and return the result in r2 + * and the syscall instruction needs + * r1 == syscall number + * r2 ... r7 == syscall arguments + * and returns the result in r2 + * Shuffle everything around appropriately. + */ + lgr %r8,%r2 /* signal_pending pointer */ + lgr %r1,%r3 /* syscall number */ + lgr %r2,%r4 /* syscall args */ + lgr %r3,%r5 + lgr %r4,%r6 + lmg %r5,%r7,320(%r15) + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + icm %r0,15,0(%r8) + jne 2f + svc 0 +safe_syscall_end: + +1: lg %r15,0(%r15) /* load back chain */ + .cfi_remember_state + .cfi_adjust_cfa_offset -160 + lmg %r6,%r15,48(%r15) /* load saved registers */ + br %r14 + .cfi_restore_state +2: lghi %r2, -TARGET_ERESTARTSYS + j 1b + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/host/sparc/host-signal.h b/linux-user/host/sparc/host-signal.h new file mode 100644 index 000000000..734293607 --- /dev/null +++ b/linux-user/host/sparc/host-signal.h @@ -0,0 +1,63 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_HOST_SIGNAL_H +#define SPARC_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ +#ifdef __arch64__ + return uc->uc_mcontext.mc_gregs[MC_PC]; +#else + return uc->uc_mcontext.gregs[REG_PC]; +#endif +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ +#ifdef __arch64__ + uc->uc_mcontext.mc_gregs[MC_PC] = pc; +#else + uc->uc_mcontext.gregs[REG_PC] = pc; +#endif +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + uint32_t insn = *(uint32_t *)host_signal_pc(uc); + + if ((insn >> 30) == 3) { + switch ((insn >> 19) & 0x3f) { + case 0x05: /* stb */ + case 0x15: /* stba */ + case 0x06: /* sth */ + case 0x16: /* stha */ + case 0x04: /* st */ + case 0x14: /* sta */ + case 0x07: /* std */ + case 0x17: /* stda */ + case 0x0e: /* stx */ + case 0x1e: /* stxa */ + case 0x24: /* stf */ + case 0x34: /* stfa */ + case 0x27: /* stdf */ + case 0x37: /* stdfa */ + case 0x26: /* stqf */ + case 0x36: /* stqfa */ + case 0x25: /* stfsr */ + case 0x3c: /* casa */ + case 0x3e: /* casxa */ + return true; + } + } + return false; +} + +#endif diff --git a/linux-user/host/sparc/hostdep.h b/linux-user/host/sparc/hostdep.h new file mode 100644 index 000000000..391ad923c --- /dev/null +++ b/linux-user/host/sparc/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_HOSTDEP_H +#define SPARC_HOSTDEP_H + +#endif diff --git a/linux-user/host/sparc64/host-signal.h b/linux-user/host/sparc64/host-signal.h new file mode 100644 index 000000000..1191fe2d4 --- /dev/null +++ b/linux-user/host/sparc64/host-signal.h @@ -0,0 +1 @@ +#include "../sparc/host-signal.h" diff --git a/linux-user/host/sparc64/hostdep.h b/linux-user/host/sparc64/hostdep.h new file mode 100644 index 000000000..ce3968fca --- /dev/null +++ b/linux-user/host/sparc64/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef SPARC64_HOSTDEP_H +#define SPARC64_HOSTDEP_H + +#endif diff --git a/linux-user/host/x32/host-signal.h b/linux-user/host/x32/host-signal.h new file mode 100644 index 000000000..26800591d --- /dev/null +++ b/linux-user/host/x32/host-signal.h @@ -0,0 +1 @@ +#include "../x86_64/host-signal.h" diff --git a/linux-user/host/x32/hostdep.h b/linux-user/host/x32/hostdep.h new file mode 100644 index 000000000..2c2d6d37d --- /dev/null +++ b/linux-user/host/x32/hostdep.h @@ -0,0 +1,15 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef X32_HOSTDEP_H +#define X32_HOSTDEP_H + +#endif diff --git a/linux-user/host/x86_64/host-signal.h b/linux-user/host/x86_64/host-signal.h new file mode 100644 index 000000000..c71d597eb --- /dev/null +++ b/linux-user/host/x86_64/host-signal.h @@ -0,0 +1,29 @@ +/* + * host-signal.h: signal info dependent on the host architecture + * + * Copyright (C) 2021 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef X86_64_HOST_SIGNAL_H +#define X86_64_HOST_SIGNAL_H + +static inline uintptr_t host_signal_pc(ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_RIP]; +} + +static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc) +{ + uc->uc_mcontext.gregs[REG_RIP] = pc; +} + +static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc) +{ + return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe + && (uc->uc_mcontext.gregs[REG_ERR] & 0x2); +} + +#endif diff --git a/linux-user/host/x86_64/hostdep.h b/linux-user/host/x86_64/hostdep.h new file mode 100644 index 000000000..9c62bd26b --- /dev/null +++ b/linux-user/host/x86_64/hostdep.h @@ -0,0 +1,18 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * * Written by Peter Maydell <peter.maydell@linaro.org> + * + * Copyright (C) 2016 Linaro Limited + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef X86_64_HOSTDEP_H +#define X86_64_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#endif diff --git a/linux-user/host/x86_64/safe-syscall.inc.S b/linux-user/host/x86_64/safe-syscall.inc.S new file mode 100644 index 000000000..f36992daa --- /dev/null +++ b/linux-user/host/x86_64/safe-syscall.inc.S @@ -0,0 +1,91 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Copyright (C) 2015 Timothy Edward Baldwin <T.E.Baldwin99@members.leeds.ac.uk> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + + /* This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + /* This saves a frame pointer and aligns the stack for the syscall. + * (It's unclear if the syscall ABI has the same stack alignment + * requirements as the userspace function call ABI, but better safe than + * sorry. Appendix A2 of http://www.x86-64.org/documentation/abi.pdf + * does not list any ABI differences regarding stack alignment.) + */ + push %rbp + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset rbp, 0 + + /* The syscall calling convention isn't the same as the + * C one: + * we enter with rdi == *signal_pending + * rsi == syscall number + * rdx, rcx, r8, r9, (stack), (stack) == syscall arguments + * and return the result in rax + * and the syscall instruction needs + * rax == syscall number + * rdi, rsi, rdx, r10, r8, r9 == syscall arguments + * and returns the result in rax + * Shuffle everything around appropriately. + * Note that syscall will trash rcx and r11. + */ + mov %rsi, %rax /* syscall number */ + mov %rdi, %rbp /* signal_pending pointer */ + /* and the syscall arguments */ + mov %rdx, %rdi + mov %rcx, %rsi + mov %r8, %rdx + mov %r9, %r10 + mov 16(%rsp), %r8 + mov 24(%rsp), %r9 + + /* This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* if signal_pending is non-zero, don't do the call */ + cmpl $0, (%rbp) + jnz 1f + syscall +safe_syscall_end: + /* code path for having successfully executed the syscall */ + pop %rbp + .cfi_remember_state + .cfi_def_cfa_offset 8 + .cfi_restore rbp + ret + +1: + /* code path when we didn't execute the syscall */ + .cfi_restore_state + mov $-TARGET_ERESTARTSYS, %rax + pop %rbp + .cfi_def_cfa_offset 8 + .cfi_restore rbp + ret + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base |