aboutsummaryrefslogtreecommitdiff
path: root/linux-user/i386/signal.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/i386/signal.c')
-rw-r--r--linux-user/i386/signal.c668
1 files changed, 399 insertions, 269 deletions
diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c
index cfe70fc5cf..cb90711834 100644
--- a/linux-user/i386/signal.c
+++ b/linux-user/i386/signal.c
@@ -21,6 +21,7 @@
#include "user-internals.h"
#include "signal-common.h"
#include "linux-user/trace.h"
+#include "user/tswap-target.h"
/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */
@@ -33,15 +34,22 @@ struct target_fpreg {
uint16_t exponent;
};
-struct target_fpxreg {
- uint16_t significand[4];
- uint16_t exponent;
- uint16_t padding[3];
-};
+/* Legacy x87 fpu state format for FSAVE/FRESTOR. */
+struct target_fregs_state {
+ uint32_t cwd;
+ uint32_t swd;
+ uint32_t twd;
+ uint32_t fip;
+ uint32_t fcs;
+ uint32_t foo;
+ uint32_t fos;
+ struct target_fpreg st[8];
-struct target_xmmreg {
- uint32_t element[4];
+ /* Software status information [not touched by FSAVE]. */
+ uint16_t status;
+ uint16_t magic; /* 0xffff: FPU data only, 0x0000: FXSR FPU data */
};
+QEMU_BUILD_BUG_ON(sizeof(struct target_fregs_state) != 32 + 80);
struct target_fpx_sw_bytes {
uint32_t magic1;
@@ -52,55 +60,11 @@ struct target_fpx_sw_bytes {
};
QEMU_BUILD_BUG_ON(sizeof(struct target_fpx_sw_bytes) != 12*4);
-struct target_fpstate_fxsave {
- /* FXSAVE format */
- uint16_t cw;
- uint16_t sw;
- uint16_t twd;
- uint16_t fop;
- uint64_t rip;
- uint64_t rdp;
- uint32_t mxcsr;
- uint32_t mxcsr_mask;
- uint32_t st_space[32];
- uint32_t xmm_space[64];
- uint32_t hw_reserved[12];
- struct target_fpx_sw_bytes sw_reserved;
- uint8_t xfeatures[];
-};
-#define TARGET_FXSAVE_SIZE sizeof(struct target_fpstate_fxsave)
-QEMU_BUILD_BUG_ON(TARGET_FXSAVE_SIZE != 512);
-QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_fxsave, sw_reserved) != 464);
-
struct target_fpstate_32 {
- /* Regular FPU environment */
- uint32_t cw;
- uint32_t sw;
- uint32_t tag;
- uint32_t ipoff;
- uint32_t cssel;
- uint32_t dataoff;
- uint32_t datasel;
- struct target_fpreg st[8];
- uint16_t status;
- uint16_t magic; /* 0xffff = regular FPU data only */
- struct target_fpstate_fxsave fxsave;
+ struct target_fregs_state fpstate;
+ X86LegacyXSaveArea fxstate;
};
-/*
- * For simplicity, setup_frame aligns struct target_fpstate_32 to
- * 16 bytes, so ensure that the FXSAVE area is also aligned.
- */
-QEMU_BUILD_BUG_ON(offsetof(struct target_fpstate_32, fxsave) & 15);
-
-#ifndef TARGET_X86_64
-# define target_fpstate target_fpstate_32
-# define TARGET_FPSTATE_FXSAVE_OFFSET offsetof(struct target_fpstate_32, fxsave)
-#else
-# define target_fpstate target_fpstate_fxsave
-# define TARGET_FPSTATE_FXSAVE_OFFSET 0
-#endif
-
struct target_sigcontext_32 {
uint16_t gs, __gsh;
uint16_t fs, __fsh;
@@ -183,24 +147,16 @@ struct sigframe {
int sig;
struct target_sigcontext sc;
/*
- * The actual fpstate is placed after retcode[] below, to make
- * room for the variable-sized xsave data. The older unused fpstate
- * has to be kept to avoid changing the offset of extramask[], which
+ * The actual fpstate is placed after retcode[] below, to make room
+ * for the variable-sized xsave data. The older unused fpstate has
+ * to be kept to avoid changing the offset of extramask[], which
* is part of the ABI.
*/
- struct target_fpstate fpstate_unused;
+ struct target_fpstate_32 fpstate_unused;
abi_ulong extramask[TARGET_NSIG_WORDS-1];
char retcode[8];
-
- /*
- * This field will be 16-byte aligned in memory. Applying QEMU_ALIGNED
- * to it ensures that the base of the frame has an appropriate alignment
- * too.
- */
- struct target_fpstate fpstate QEMU_ALIGNED(8);
+ /* fp state follows here */
};
-#define TARGET_SIGFRAME_FXSAVE_OFFSET ( \
- offsetof(struct sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
struct rt_sigframe {
abi_ulong pretcode;
@@ -210,10 +166,8 @@ struct rt_sigframe {
struct target_siginfo info;
struct target_ucontext uc;
char retcode[8];
- struct target_fpstate fpstate QEMU_ALIGNED(8);
+ /* fp state follows here */
};
-#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
- offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
/*
* Verify that vdso-asmoffset.h constants match.
@@ -231,64 +185,175 @@ struct rt_sigframe {
abi_ulong pretcode;
struct target_ucontext uc;
struct target_siginfo info;
- struct target_fpstate fpstate QEMU_ALIGNED(16);
+ /* fp state follows here */
};
-#define TARGET_RT_SIGFRAME_FXSAVE_OFFSET ( \
- offsetof(struct rt_sigframe, fpstate) + TARGET_FPSTATE_FXSAVE_OFFSET)
#endif
+typedef enum {
+#ifndef TARGET_X86_64
+ FPSTATE_FSAVE,
+#endif
+ FPSTATE_FXSAVE,
+ FPSTATE_XSAVE
+} FPStateKind;
+
+static FPStateKind get_fpstate_kind(CPUX86State *env)
+{
+ if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
+ return FPSTATE_XSAVE;
+ }
+#ifdef TARGET_X86_64
+ return FPSTATE_FXSAVE;
+#else
+ if (env->features[FEAT_1_EDX] & CPUID_FXSR) {
+ return FPSTATE_FXSAVE;
+ }
+ return FPSTATE_FSAVE;
+#endif
+}
+
+static unsigned get_fpstate_size(CPUX86State *env, FPStateKind fpkind)
+{
+ /*
+ * Kernel:
+ * fpu__alloc_mathframe
+ * xstate_sigframe_size(current->thread.fpu.fpstate);
+ * size = fpstate->user_size
+ * use_xsave() ? size + FP_XSTATE_MAGIC2_SIZE : size
+ * where fpstate->user_size is computed at init in
+ * fpu__init_system_xstate_size_legacy and
+ * fpu__init_system_xstate.
+ *
+ * Here we have no place to pre-compute, so inline it all.
+ */
+ switch (fpkind) {
+ case FPSTATE_XSAVE:
+ return (xsave_area_size(env->xcr0, false)
+ + TARGET_FP_XSTATE_MAGIC2_SIZE);
+ case FPSTATE_FXSAVE:
+ return sizeof(X86LegacyXSaveArea);
+#ifndef TARGET_X86_64
+ case FPSTATE_FSAVE:
+ return sizeof(struct target_fregs_state);
+#endif
+ }
+ g_assert_not_reached();
+}
+
+static abi_ptr get_sigframe(struct target_sigaction *ka, CPUX86State *env,
+ unsigned frame_size, FPStateKind fpkind,
+ abi_ptr *fpstate, abi_ptr *fxstate, abi_ptr *fpend)
+{
+ abi_ptr sp;
+ unsigned math_size;
+
+ /* Default to using normal stack */
+ sp = get_sp_from_cpustate(env);
+#ifdef TARGET_X86_64
+ sp -= 128; /* this is the redzone */
+#endif
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa_flags & TARGET_SA_ONSTACK) {
+ sp = target_sigsp(sp, ka);
+ } else {
+#ifndef TARGET_X86_64
+ /* This is the legacy signal stack switching. */
+ if ((env->segs[R_SS].selector & 0xffff) != __USER_DS
+ && !(ka->sa_flags & TARGET_SA_RESTORER)
+ && ka->sa_restorer) {
+ sp = ka->sa_restorer;
+ }
+#endif
+ }
+
+ math_size = get_fpstate_size(env, fpkind);
+ sp = ROUND_DOWN(sp - math_size, 64);
+ *fpend = sp + math_size;
+ *fxstate = sp;
+#ifndef TARGET_X86_64
+ if (fpkind != FPSTATE_FSAVE) {
+ sp -= sizeof(struct target_fregs_state);
+ }
+#endif
+ *fpstate = sp;
+
+ sp -= frame_size;
+ /*
+ * Align the stack pointer according to the ABI, i.e. so that on
+ * function entry ((sp + sizeof(return_addr)) & 15) == 0.
+ */
+ sp += sizeof(target_ulong);
+ sp = ROUND_DOWN(sp, 16);
+ sp -= sizeof(target_ulong);
+
+ return sp;
+}
+
/*
* Set up a signal frame.
*/
-static void xsave_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave,
- abi_ulong fxsave_addr)
+static void fxsave_sigcontext(CPUX86State *env, X86LegacyXSaveArea *fxstate)
{
- if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) {
- /* fxsave_addr must be 16 byte aligned for fxsave */
- assert(!(fxsave_addr & 0xf));
+ struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved;
- cpu_x86_fxsave(env, fxsave_addr);
- __put_user(0, &fxsave->sw_reserved.magic1);
- } else {
- uint32_t xstate_size = xsave_area_size(env->xcr0, false);
- uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE;
+ cpu_x86_fxsave(env, fxstate, sizeof(*fxstate));
+ __put_user(0, &sw->magic1);
+}
- /*
- * extended_size is the offset from fpstate_addr to right after the end
- * of the extended save states. On 32-bit that includes the legacy
- * FSAVE area.
- */
- uint32_t extended_size = TARGET_FPSTATE_FXSAVE_OFFSET
- + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE;
-
- /* fxsave_addr must be 64 byte aligned for xsave */
- assert(!(fxsave_addr & 0x3f));
-
- /* Zero the header, XSAVE *adds* features to an existing save state. */
- memset(fxsave->xfeatures, 0, 64);
- cpu_x86_xsave(env, fxsave_addr);
- __put_user(TARGET_FP_XSTATE_MAGIC1, &fxsave->sw_reserved.magic1);
- __put_user(extended_size, &fxsave->sw_reserved.extended_size);
- __put_user(env->xcr0, &fxsave->sw_reserved.xfeatures);
- __put_user(xstate_size, &fxsave->sw_reserved.xstate_size);
- __put_user(TARGET_FP_XSTATE_MAGIC2, (uint32_t *) &fxsave->xfeatures[xfeatures_size]);
- }
+static void xsave_sigcontext(CPUX86State *env,
+ X86LegacyXSaveArea *fxstate,
+ abi_ptr fpstate_addr,
+ abi_ptr xstate_addr,
+ abi_ptr fpend_addr)
+{
+ struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved;
+ /*
+ * extended_size is the offset from fpstate_addr to right after
+ * the end of the extended save states. On 32-bit that includes
+ * the legacy FSAVE area.
+ */
+ uint32_t extended_size = fpend_addr - fpstate_addr;
+ /* Recover xstate_size by removing magic2. */
+ uint32_t xstate_size = (fpend_addr - xstate_addr
+ - TARGET_FP_XSTATE_MAGIC2_SIZE);
+ /* magic2 goes just after xstate. */
+ uint32_t *magic2 = (void *)fxstate + xstate_size;
+
+ /* xstate_addr must be 64 byte aligned for xsave */
+ assert(!(xstate_addr & 0x3f));
+
+ /* Zero the header, XSAVE *adds* features to an existing save state. */
+ memset(fxstate + 1, 0, sizeof(X86XSaveHeader));
+ cpu_x86_xsave(env, fxstate, fpend_addr - xstate_addr, env->xcr0);
+
+ __put_user(TARGET_FP_XSTATE_MAGIC1, &sw->magic1);
+ __put_user(extended_size, &sw->extended_size);
+ __put_user(env->xcr0, &sw->xfeatures);
+ __put_user(xstate_size, &sw->xstate_size);
+ __put_user(TARGET_FP_XSTATE_MAGIC2, magic2);
}
-static void setup_sigcontext(struct target_sigcontext *sc,
- struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask,
- abi_ulong fpstate_addr)
+static void setup_sigcontext(CPUX86State *env,
+ struct target_sigcontext *sc,
+ abi_ulong mask, FPStateKind fpkind,
+ struct target_fregs_state *fpstate,
+ abi_ptr fpstate_addr,
+ X86LegacyXSaveArea *fxstate,
+ abi_ptr fxstate_addr,
+ abi_ptr fpend_addr)
{
CPUState *cs = env_cpu(env);
+
#ifndef TARGET_X86_64
uint16_t magic;
/* already locked in setup_frame() */
- __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs);
- __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs);
- __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es);
- __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds);
+ __put_user(env->segs[R_GS].selector, (uint32_t *)&sc->gs);
+ __put_user(env->segs[R_FS].selector, (uint32_t *)&sc->fs);
+ __put_user(env->segs[R_ES].selector, (uint32_t *)&sc->es);
+ __put_user(env->segs[R_DS].selector, (uint32_t *)&sc->ds);
__put_user(env->regs[R_EDI], &sc->edi);
__put_user(env->regs[R_ESI], &sc->esi);
__put_user(env->regs[R_EBP], &sc->ebp);
@@ -300,20 +365,14 @@ static void setup_sigcontext(struct target_sigcontext *sc,
__put_user(cs->exception_index, &sc->trapno);
__put_user(env->error_code, &sc->err);
__put_user(env->eip, &sc->eip);
- __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs);
+ __put_user(env->segs[R_CS].selector, (uint32_t *)&sc->cs);
__put_user(env->eflags, &sc->eflags);
__put_user(env->regs[R_ESP], &sc->esp_at_signal);
- __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss);
+ __put_user(env->segs[R_SS].selector, (uint32_t *)&sc->ss);
- cpu_x86_fsave(env, fpstate_addr, 1);
- fpstate->status = fpstate->sw;
- if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) {
- magic = 0xffff;
- } else {
- xsave_sigcontext(env, &fpstate->fxsave,
- fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET);
- magic = 0;
- }
+ cpu_x86_fsave(env, fpstate, sizeof(*fpstate));
+ fpstate->status = fpstate->swd;
+ magic = (fpkind == FPSTATE_FSAVE ? 0 : 0xffff);
__put_user(magic, &fpstate->magic);
#else
__put_user(env->regs[R_EDI], &sc->rdi);
@@ -343,57 +402,25 @@ static void setup_sigcontext(struct target_sigcontext *sc,
__put_user((uint16_t)0, &sc->gs);
__put_user((uint16_t)0, &sc->fs);
__put_user(env->segs[R_SS].selector, &sc->ss);
-
- xsave_sigcontext(env, fpstate, fpstate_addr);
#endif
- __put_user(fpstate_addr, &sc->fpstate);
+ switch (fpkind) {
+ case FPSTATE_XSAVE:
+ xsave_sigcontext(env, fxstate, fpstate_addr, fxstate_addr, fpend_addr);
+ break;
+ case FPSTATE_FXSAVE:
+ fxsave_sigcontext(env, fxstate);
+ break;
+ default:
+ break;
+ }
+ __put_user(fpstate_addr, &sc->fpstate);
/* non-iBCS2 extensions.. */
__put_user(mask, &sc->oldmask);
__put_user(env->cr[2], &sc->cr2);
}
-/*
- * Determine which stack to use..
- */
-
-static inline abi_ulong
-get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t fxsave_offset)
-{
- unsigned long esp;
-
- /* Default to using normal stack */
- esp = get_sp_from_cpustate(env);
-#ifdef TARGET_X86_64
- esp -= 128; /* this is the redzone */
-#endif
-
- /* This is the X/Open sanctioned signal stack switching. */
- if (ka->sa_flags & TARGET_SA_ONSTACK) {
- esp = target_sigsp(esp, ka);
- } else {
-#ifndef TARGET_X86_64
- /* This is the legacy signal stack switching. */
- if ((env->segs[R_SS].selector & 0xffff) != __USER_DS &&
- !(ka->sa_flags & TARGET_SA_RESTORER) &&
- ka->sa_restorer) {
- esp = (unsigned long) ka->sa_restorer;
- }
-#endif
- }
-
- if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) {
- return (esp - (fxsave_offset + TARGET_FXSAVE_SIZE)) & -8ul;
- } else if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) {
- return ((esp - TARGET_FXSAVE_SIZE) & -16ul) - fxsave_offset;
- } else {
- size_t xstate_size =
- xsave_area_size(env->xcr0, false) + TARGET_FP_XSTATE_MAGIC2_SIZE;
- return ((esp - xstate_size) & -64ul) - fxsave_offset;
- }
-}
-
#ifndef TARGET_X86_64
static void install_sigtramp(void *tramp)
{
@@ -415,22 +442,36 @@ static void install_rt_sigtramp(void *tramp)
void setup_frame(int sig, struct target_sigaction *ka,
target_sigset_t *set, CPUX86State *env)
{
- abi_ulong frame_addr;
+ abi_ptr frame_addr, fpstate_addr, fxstate_addr, fpend_addr;
struct sigframe *frame;
- int i;
-
- frame_addr = get_sigframe(ka, env, TARGET_SIGFRAME_FXSAVE_OFFSET);
+ struct target_fregs_state *fpstate;
+ X86LegacyXSaveArea *fxstate;
+ unsigned total_size;
+ FPStateKind fpkind;
+
+ fpkind = get_fpstate_kind(env);
+ frame_addr = get_sigframe(ka, env, sizeof(struct sigframe), fpkind,
+ &fpstate_addr, &fxstate_addr, &fpend_addr);
trace_user_setup_frame(env, frame_addr);
- if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
- goto give_sigsegv;
+ total_size = fpend_addr - frame_addr;
+ frame = lock_user(VERIFY_WRITE, frame_addr, total_size, 0);
+ if (!frame) {
+ force_sigsegv(sig);
+ return;
+ }
- __put_user(sig, &frame->sig);
+ fxstate = (void *)frame + (fxstate_addr - frame_addr);
+#ifdef TARGET_X86_64
+ fpstate = NULL;
+#else
+ fpstate = (void *)frame + (fpstate_addr - frame_addr);
+#endif
- setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0],
- frame_addr + offsetof(struct sigframe, fpstate));
+ setup_sigcontext(env, &frame->sc, set->sig[0], fpkind,
+ fpstate, fpstate_addr, fxstate, fxstate_addr, fpend_addr);
- for (i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (int i = 1; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->extramask[i - 1]);
}
@@ -443,23 +484,24 @@ void setup_frame(int sig, struct target_sigaction *ka,
install_sigtramp(frame->retcode);
__put_user(default_sigreturn, &frame->pretcode);
}
+ unlock_user(frame, frame_addr, total_size);
/* Set up registers for signal handler */
env->regs[R_ESP] = frame_addr;
env->eip = ka->_sa_handler;
+ /* Store argument for both -mregparm=3 and standard. */
+ env->regs[R_EAX] = sig;
+ __put_user(sig, &frame->sig);
+ /* The kernel clears EDX and ECX even though there is only one arg. */
+ env->regs[R_EDX] = 0;
+ env->regs[R_ECX] = 0;
+
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
cpu_x86_load_seg(env, R_CS, __USER_CS);
env->eflags &= ~TF_MASK;
-
- unlock_user_struct(frame, frame_addr, 1);
-
- return;
-
-give_sigsegv:
- force_sigsegv(sig);
}
#endif
@@ -468,48 +510,51 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
target_siginfo_t *info,
target_sigset_t *set, CPUX86State *env)
{
- abi_ulong frame_addr;
-#ifndef TARGET_X86_64
- abi_ulong addr;
-#endif
+ abi_ptr frame_addr, fpstate_addr, fxstate_addr, fpend_addr;
struct rt_sigframe *frame;
- int i;
-
- frame_addr = get_sigframe(ka, env, TARGET_RT_SIGFRAME_FXSAVE_OFFSET);
+ X86LegacyXSaveArea *fxstate;
+ struct target_fregs_state *fpstate;
+ unsigned total_size;
+ FPStateKind fpkind;
+
+ fpkind = get_fpstate_kind(env);
+ frame_addr = get_sigframe(ka, env, sizeof(struct rt_sigframe), fpkind,
+ &fpstate_addr, &fxstate_addr, &fpend_addr);
trace_user_setup_rt_frame(env, frame_addr);
- if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
+ total_size = fpend_addr - frame_addr;
+ frame = lock_user(VERIFY_WRITE, frame_addr, total_size, 0);
+ if (!frame) {
goto give_sigsegv;
+ }
- /* These fields are only in rt_sigframe on 32 bit */
-#ifndef TARGET_X86_64
- __put_user(sig, &frame->sig);
- addr = frame_addr + offsetof(struct rt_sigframe, info);
- __put_user(addr, &frame->pinfo);
- addr = frame_addr + offsetof(struct rt_sigframe, uc);
- __put_user(addr, &frame->puc);
-#endif
if (ka->sa_flags & TARGET_SA_SIGINFO) {
frame->info = *info;
}
/* Create the ucontext. */
- if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
- __put_user(1, &frame->uc.tuc_flags);
- } else {
- __put_user(0, &frame->uc.tuc_flags);
- }
+ __put_user(fpkind == FPSTATE_XSAVE, &frame->uc.tuc_flags);
__put_user(0, &frame->uc.tuc_link);
target_save_altstack(&frame->uc.tuc_stack, env);
- setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env,
- set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate));
- for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+ fxstate = (void *)frame + (fxstate_addr - frame_addr);
+#ifdef TARGET_X86_64
+ fpstate = NULL;
+#else
+ fpstate = (void *)frame + (fpstate_addr - frame_addr);
+#endif
+
+ setup_sigcontext(env, &frame->uc.tuc_mcontext, set->sig[0], fpkind,
+ fpstate, fpstate_addr, fxstate, fxstate_addr, fpend_addr);
+
+ for (int i = 0; i < TARGET_NSIG_WORDS; i++) {
__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
}
- /* Set up to return from userspace. If provided, use a stub
- already in userspace. */
+ /*
+ * Set up to return from userspace. If provided, use a stub
+ * already in userspace.
+ */
if (ka->sa_flags & TARGET_SA_RESTORER) {
__put_user(ka->sa_restorer, &frame->pretcode);
} else {
@@ -528,63 +573,148 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
env->eip = ka->_sa_handler;
#ifndef TARGET_X86_64
+ /* Store arguments for both -mregparm=3 and standard. */
env->regs[R_EAX] = sig;
+ __put_user(sig, &frame->sig);
env->regs[R_EDX] = frame_addr + offsetof(struct rt_sigframe, info);
+ __put_user(env->regs[R_EDX], &frame->pinfo);
env->regs[R_ECX] = frame_addr + offsetof(struct rt_sigframe, uc);
+ __put_user(env->regs[R_ECX], &frame->puc);
#else
env->regs[R_EAX] = 0;
env->regs[R_EDI] = sig;
env->regs[R_ESI] = frame_addr + offsetof(struct rt_sigframe, info);
env->regs[R_EDX] = frame_addr + offsetof(struct rt_sigframe, uc);
#endif
+ unlock_user(frame, frame_addr, total_size);
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
env->eflags &= ~TF_MASK;
-
- unlock_user_struct(frame, frame_addr, 1);
-
return;
give_sigsegv:
force_sigsegv(sig);
}
-static int xrstor_sigcontext(CPUX86State *env, struct target_fpstate_fxsave *fxsave,
- abi_ulong fxsave_addr)
+/*
+ * Restore a signal frame.
+ */
+
+static bool xrstor_sigcontext(CPUX86State *env, FPStateKind fpkind,
+ X86LegacyXSaveArea *fxstate,
+ abi_ptr fxstate_addr)
+{
+ struct target_fpx_sw_bytes *sw = (void *)&fxstate->sw_reserved;
+ uint32_t magic1, magic2;
+ uint32_t extended_size, xstate_size, min_size, max_size;
+ uint64_t xfeatures;
+ void *xstate;
+ bool ok;
+
+ switch (fpkind) {
+ case FPSTATE_XSAVE:
+ magic1 = tswap32(sw->magic1);
+ extended_size = tswap32(sw->extended_size);
+ xstate_size = tswap32(sw->xstate_size);
+ min_size = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader);
+ max_size = xsave_area_size(env->xcr0, false);
+
+ /* Check for the first magic field and other error scenarios. */
+ if (magic1 != TARGET_FP_XSTATE_MAGIC1 ||
+ xstate_size < min_size ||
+ xstate_size > max_size ||
+ xstate_size > extended_size) {
+ break;
+ }
+
+ /*
+ * Restore the features indicated in the frame, masked by
+ * those currently enabled. Re-check the frame size.
+ * ??? It is not clear where the kernel does this, but it
+ * is not in check_xstate_in_sigframe, and so (probably)
+ * does not fall back to fxrstor.
+ */
+ xfeatures = tswap64(sw->xfeatures) & env->xcr0;
+ min_size = xsave_area_size(xfeatures, false);
+ if (xstate_size < min_size) {
+ return false;
+ }
+
+ /* Re-lock the entire xstate area, with the extensions and magic. */
+ xstate = lock_user(VERIFY_READ, fxstate_addr,
+ xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE, 1);
+ if (!xstate) {
+ return false;
+ }
+
+ /*
+ * Check for the presence of second magic word at the end of memory
+ * layout. This detects the case where the user just copied the legacy
+ * fpstate layout with out copying the extended state information
+ * in the memory layout.
+ */
+ magic2 = tswap32(*(uint32_t *)(xstate + xstate_size));
+ if (magic2 != TARGET_FP_XSTATE_MAGIC2) {
+ unlock_user(xstate, fxstate_addr, 0);
+ break;
+ }
+
+ ok = cpu_x86_xrstor(env, xstate, xstate_size, xfeatures);
+ unlock_user(xstate, fxstate_addr, 0);
+ return ok;
+
+ default:
+ break;
+ }
+
+ cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate));
+ return true;
+}
+
+#ifndef TARGET_X86_64
+static bool frstor_sigcontext(CPUX86State *env, FPStateKind fpkind,
+ struct target_fregs_state *fpstate,
+ abi_ptr fpstate_addr,
+ X86LegacyXSaveArea *fxstate,
+ abi_ptr fxstate_addr)
{
- if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) {
- uint32_t extended_size = tswapl(fxsave->sw_reserved.extended_size);
- uint32_t xstate_size = tswapl(fxsave->sw_reserved.xstate_size);
- uint32_t xfeatures_size = xstate_size - TARGET_FXSAVE_SIZE;
-
- /* Linux checks MAGIC2 using xstate_size, not extended_size. */
- if (tswapl(fxsave->sw_reserved.magic1) == TARGET_FP_XSTATE_MAGIC1 &&
- extended_size >= TARGET_FPSTATE_FXSAVE_OFFSET + xstate_size + TARGET_FP_XSTATE_MAGIC2_SIZE) {
- if (!access_ok(env_cpu(env), VERIFY_READ, fxsave_addr,
- extended_size - TARGET_FPSTATE_FXSAVE_OFFSET)) {
- return 1;
- }
- if (tswapl(*(uint32_t *) &fxsave->xfeatures[xfeatures_size]) == TARGET_FP_XSTATE_MAGIC2) {
- cpu_x86_xrstor(env, fxsave_addr);
- return 0;
- }
+ switch (fpkind) {
+ case FPSTATE_XSAVE:
+ if (!xrstor_sigcontext(env, fpkind, fxstate, fxstate_addr)) {
+ return false;
}
- /* fall through to fxrstor */
+ break;
+ case FPSTATE_FXSAVE:
+ cpu_x86_fxrstor(env, fxstate, sizeof(*fxstate));
+ break;
+ case FPSTATE_FSAVE:
+ break;
+ default:
+ g_assert_not_reached();
}
- cpu_x86_fxrstor(env, fxsave_addr);
- return 0;
+ /*
+ * Copy the legacy state because the FP portion of the FX frame has
+ * to be ignored for histerical raisins. The kernel folds the two
+ * states together and then performs a single load; here we perform
+ * the merge within ENV by loading XSTATE/FXSTATE first, then
+ * overriding with the FSTATE afterward.
+ */
+ cpu_x86_frstor(env, fpstate, sizeof(*fpstate));
+ return true;
}
+#endif
-static int
-restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
+static bool restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
{
- int err = 1;
- abi_ulong fpstate_addr;
- unsigned int tmpflags;
+ abi_ptr fpstate_addr;
+ unsigned tmpflags, math_size;
+ FPStateKind fpkind;
+ void *fpstate;
+ bool ok;
#ifndef TARGET_X86_64
cpu_x86_load_seg(env, R_GS, tswap16(sc->gs));
@@ -629,32 +759,34 @@ restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc)
tmpflags = tswapl(sc->eflags);
env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5);
- // regs->orig_eax = -1; /* disable syscall checks */
fpstate_addr = tswapl(sc->fpstate);
- if (fpstate_addr != 0) {
- struct target_fpstate *fpstate;
- if (!lock_user_struct(VERIFY_READ, fpstate, fpstate_addr,
- sizeof(struct target_fpstate))) {
- return err;
- }
+ if (fpstate_addr == 0) {
+ return true;
+ }
+
+ fpkind = get_fpstate_kind(env);
+ math_size = get_fpstate_size(env, fpkind);
#ifndef TARGET_X86_64
- if (!(env->features[FEAT_1_EDX] & CPUID_FXSR)) {
- cpu_x86_frstor(env, fpstate_addr, 1);
- err = 0;
- } else {
- err = xrstor_sigcontext(env, &fpstate->fxsave,
- fpstate_addr + TARGET_FPSTATE_FXSAVE_OFFSET);
- }
-#else
- err = xrstor_sigcontext(env, fpstate, fpstate_addr);
+ if (fpkind != FPSTATE_FSAVE) {
+ math_size += sizeof(struct target_fregs_state);
+ }
#endif
- unlock_user_struct(fpstate, fpstate_addr, 0);
- } else {
- err = 0;
+ fpstate = lock_user(VERIFY_READ, fpstate_addr, math_size, 1);
+ if (!fpstate) {
+ return false;
}
- return err;
+#ifdef TARGET_X86_64
+ ok = xrstor_sigcontext(env, fpkind, fpstate, fpstate_addr);
+#else
+ ok = frstor_sigcontext(env, fpkind, fpstate, fpstate_addr,
+ fpstate + sizeof(struct target_fregs_state),
+ fpstate_addr + sizeof(struct target_fregs_state));
+#endif
+
+ unlock_user(fpstate, fpstate_addr, 0);
+ return ok;
}
/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */
@@ -665,29 +797,27 @@ long do_sigreturn(CPUX86State *env)
abi_ulong frame_addr = env->regs[R_ESP] - 8;
target_sigset_t target_set;
sigset_t set;
- int i;
trace_user_do_sigreturn(env, frame_addr);
- if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
- goto badframe;
- /* set blocked signals */
+ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+ force_sig(TARGET_SIGSEGV);
+ return -QEMU_ESIGRETURN;
+ }
+
+ /* Set blocked signals. */
__get_user(target_set.sig[0], &frame->sc.oldmask);
- for(i = 1; i < TARGET_NSIG_WORDS; i++) {
+ for (int i = 1; i < TARGET_NSIG_WORDS; i++) {
__get_user(target_set.sig[i], &frame->extramask[i - 1]);
}
-
target_to_host_sigset_internal(&set, &target_set);
set_sigmask(&set);
- /* restore registers */
- if (restore_sigcontext(env, &frame->sc))
- goto badframe;
- unlock_user_struct(frame, frame_addr, 0);
- return -QEMU_ESIGRETURN;
+ /* Restore registers */
+ if (!restore_sigcontext(env, &frame->sc)) {
+ force_sig(TARGET_SIGSEGV);
+ }
-badframe:
unlock_user_struct(frame, frame_addr, 0);
- force_sig(TARGET_SIGSEGV);
return -QEMU_ESIGRETURN;
}
#endif
@@ -705,7 +835,7 @@ long do_rt_sigreturn(CPUX86State *env)
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
- if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) {
+ if (!restore_sigcontext(env, &frame->uc.tuc_mcontext)) {
goto badframe;
}