mirror of https://github.com/torvalds/linux.git
261 lines
7.3 KiB
C
261 lines
7.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#define __FRAME_OFFSETS
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <sys/ucontext.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/sigcontext.h>
|
|
#include <sysdep/ptrace.h>
|
|
#include <sysdep/mcontext.h>
|
|
#include <arch.h>
|
|
|
|
void get_regs_from_mc(struct uml_pt_regs *regs, mcontext_t *mc)
|
|
{
|
|
#ifdef __i386__
|
|
#define COPY2(X,Y) regs->gp[X] = mc->gregs[REG_##Y]
|
|
#define COPY(X) regs->gp[X] = mc->gregs[REG_##X]
|
|
#define COPY_SEG(X) regs->gp[X] = mc->gregs[REG_##X] & 0xffff;
|
|
#define COPY_SEG_CPL3(X) regs->gp[X] = (mc->gregs[REG_##X] & 0xffff) | 3;
|
|
COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS);
|
|
COPY(EDI); COPY(ESI); COPY(EBP);
|
|
COPY2(UESP, ESP); /* sic */
|
|
COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX);
|
|
COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS);
|
|
#undef COPY2
|
|
#undef COPY
|
|
#undef COPY_SEG
|
|
#undef COPY_SEG_CPL3
|
|
#else
|
|
#define COPY2(X,Y) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##Y]
|
|
#define COPY(X) regs->gp[X/sizeof(unsigned long)] = mc->gregs[REG_##X]
|
|
COPY(R8); COPY(R9); COPY(R10); COPY(R11);
|
|
COPY(R12); COPY(R13); COPY(R14); COPY(R15);
|
|
COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX);
|
|
COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP);
|
|
COPY(RIP);
|
|
COPY2(EFLAGS, EFL);
|
|
COPY2(CS, CSGSFS);
|
|
regs->gp[SS / sizeof(unsigned long)] = mc->gregs[REG_CSGSFS] >> 48;
|
|
#undef COPY2
|
|
#undef COPY
|
|
#endif
|
|
}
|
|
|
|
void mc_set_rip(void *_mc, void *target)
|
|
{
|
|
mcontext_t *mc = _mc;
|
|
|
|
#ifdef __i386__
|
|
mc->gregs[REG_EIP] = (unsigned long)target;
|
|
#else
|
|
mc->gregs[REG_RIP] = (unsigned long)target;
|
|
#endif
|
|
}
|
|
|
|
/* Same thing, but the copy macros are turned around. */
|
|
void get_mc_from_regs(struct uml_pt_regs *regs, mcontext_t *mc, int single_stepping)
|
|
{
|
|
#ifdef __i386__
|
|
#define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X]
|
|
#define COPY(X) mc->gregs[REG_##X] = regs->gp[X]
|
|
#define COPY_SEG(X) mc->gregs[REG_##X] = regs->gp[X] & 0xffff;
|
|
#define COPY_SEG_CPL3(X) mc->gregs[REG_##X] = (regs->gp[X] & 0xffff) | 3;
|
|
COPY_SEG(GS); COPY_SEG(FS); COPY_SEG(ES); COPY_SEG(DS);
|
|
COPY(EDI); COPY(ESI); COPY(EBP);
|
|
COPY2(UESP, ESP); /* sic */
|
|
COPY(EBX); COPY(EDX); COPY(ECX); COPY(EAX);
|
|
COPY(EIP); COPY_SEG_CPL3(CS); COPY(EFL); COPY_SEG_CPL3(SS);
|
|
#else
|
|
#define COPY2(X,Y) mc->gregs[REG_##Y] = regs->gp[X/sizeof(unsigned long)]
|
|
#define COPY(X) mc->gregs[REG_##X] = regs->gp[X/sizeof(unsigned long)]
|
|
COPY(R8); COPY(R9); COPY(R10); COPY(R11);
|
|
COPY(R12); COPY(R13); COPY(R14); COPY(R15);
|
|
COPY(RDI); COPY(RSI); COPY(RBP); COPY(RBX);
|
|
COPY(RDX); COPY(RAX); COPY(RCX); COPY(RSP);
|
|
COPY(RIP);
|
|
COPY2(EFLAGS, EFL);
|
|
mc->gregs[REG_CSGSFS] = mc->gregs[REG_CSGSFS] & 0xffffffffffffl;
|
|
mc->gregs[REG_CSGSFS] |= (regs->gp[SS / sizeof(unsigned long)] & 0xffff) << 48;
|
|
#endif
|
|
|
|
if (single_stepping)
|
|
mc->gregs[REG_EFL] |= X86_EFLAGS_TF;
|
|
else
|
|
mc->gregs[REG_EFL] &= ~X86_EFLAGS_TF;
|
|
}
|
|
|
|
#ifdef CONFIG_X86_32
|
|
struct _xstate_64 {
|
|
struct _fpstate_64 fpstate;
|
|
struct _header xstate_hdr;
|
|
struct _ymmh_state ymmh;
|
|
/* New processor state extensions go here: */
|
|
};
|
|
|
|
/* Not quite the right structures as these contain more information */
|
|
int um_i387_from_fxsr(struct _fpstate_32 *i387,
|
|
const struct _fpstate_64 *fxsave);
|
|
int um_fxsr_from_i387(struct _fpstate_64 *fxsave,
|
|
const struct _fpstate_32 *from);
|
|
#else
|
|
#define _xstate_64 _xstate
|
|
#endif
|
|
|
|
static struct _fpstate *get_fpstate(struct stub_data *data,
|
|
mcontext_t *mcontext,
|
|
int *fp_size)
|
|
{
|
|
struct _fpstate *res;
|
|
|
|
/* Assume floating point registers are on the same page */
|
|
res = (void *)(((unsigned long)mcontext->fpregs &
|
|
(UM_KERN_PAGE_SIZE - 1)) +
|
|
(unsigned long)&data->sigstack[0]);
|
|
|
|
if ((void *)res + sizeof(struct _fpstate) >
|
|
(void *)data->sigstack + sizeof(data->sigstack))
|
|
return NULL;
|
|
|
|
if (res->sw_reserved.magic1 != FP_XSTATE_MAGIC1) {
|
|
*fp_size = sizeof(struct _fpstate);
|
|
} else {
|
|
char *magic2_addr;
|
|
|
|
magic2_addr = (void *)res;
|
|
magic2_addr += res->sw_reserved.extended_size;
|
|
magic2_addr -= FP_XSTATE_MAGIC2_SIZE;
|
|
|
|
/* We still need to be within our stack */
|
|
if ((void *)magic2_addr >
|
|
(void *)data->sigstack + sizeof(data->sigstack))
|
|
return NULL;
|
|
|
|
/* If we do not read MAGIC2, then we did something wrong */
|
|
if (*(__u32 *)magic2_addr != FP_XSTATE_MAGIC2)
|
|
return NULL;
|
|
|
|
/* Remove MAGIC2 from the size, we do not save/restore it */
|
|
*fp_size = res->sw_reserved.extended_size -
|
|
FP_XSTATE_MAGIC2_SIZE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
int get_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
|
|
unsigned long *fp_size_out)
|
|
{
|
|
mcontext_t *mcontext;
|
|
struct _fpstate *fpstate_stub;
|
|
struct _xstate_64 *xstate_stub;
|
|
int fp_size, xstate_size;
|
|
|
|
/* mctx_offset is verified by wait_stub_done_seccomp */
|
|
mcontext = (void *)&data->sigstack[data->mctx_offset];
|
|
|
|
get_regs_from_mc(regs, mcontext);
|
|
|
|
fpstate_stub = get_fpstate(data, mcontext, &fp_size);
|
|
if (!fpstate_stub)
|
|
return -EINVAL;
|
|
|
|
#ifdef CONFIG_X86_32
|
|
xstate_stub = (void *)&fpstate_stub->_fxsr_env;
|
|
xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env);
|
|
#else
|
|
xstate_stub = (void *)fpstate_stub;
|
|
xstate_size = fp_size;
|
|
#endif
|
|
|
|
if (fp_size_out)
|
|
*fp_size_out = xstate_size;
|
|
|
|
if (xstate_size > host_fp_size)
|
|
return -ENOSPC;
|
|
|
|
memcpy(®s->fp, xstate_stub, xstate_size);
|
|
|
|
/* We do not need to read the x86_64 FS_BASE/GS_BASE registers as
|
|
* we do not permit userspace to set them directly.
|
|
*/
|
|
|
|
#ifdef CONFIG_X86_32
|
|
/* Read the i387 legacy FP registers */
|
|
if (um_fxsr_from_i387((void *)®s->fp, fpstate_stub))
|
|
return -EINVAL;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Copied because we cannot include regset.h here. */
|
|
struct task_struct;
|
|
struct user_regset;
|
|
struct membuf {
|
|
void *p;
|
|
size_t left;
|
|
};
|
|
|
|
int fpregs_legacy_get(struct task_struct *target,
|
|
const struct user_regset *regset,
|
|
struct membuf to);
|
|
|
|
int set_stub_state(struct uml_pt_regs *regs, struct stub_data *data,
|
|
int single_stepping)
|
|
{
|
|
mcontext_t *mcontext;
|
|
struct _fpstate *fpstate_stub;
|
|
struct _xstate_64 *xstate_stub;
|
|
int fp_size, xstate_size;
|
|
|
|
/* mctx_offset is verified by wait_stub_done_seccomp */
|
|
mcontext = (void *)&data->sigstack[data->mctx_offset];
|
|
|
|
if ((unsigned long)mcontext < (unsigned long)data->sigstack ||
|
|
(unsigned long)mcontext >
|
|
(unsigned long) data->sigstack +
|
|
sizeof(data->sigstack) - sizeof(*mcontext))
|
|
return -EINVAL;
|
|
|
|
get_mc_from_regs(regs, mcontext, single_stepping);
|
|
|
|
fpstate_stub = get_fpstate(data, mcontext, &fp_size);
|
|
if (!fpstate_stub)
|
|
return -EINVAL;
|
|
|
|
#ifdef CONFIG_X86_32
|
|
xstate_stub = (void *)&fpstate_stub->_fxsr_env;
|
|
xstate_size = fp_size - offsetof(struct _fpstate_32, _fxsr_env);
|
|
#else
|
|
xstate_stub = (void *)fpstate_stub;
|
|
xstate_size = fp_size;
|
|
#endif
|
|
|
|
memcpy(xstate_stub, ®s->fp, xstate_size);
|
|
|
|
#ifdef __i386__
|
|
/*
|
|
* On x86, the GDT entries are updated by arch_set_tls.
|
|
*/
|
|
|
|
/* Store the i387 legacy FP registers which the host will use */
|
|
if (um_i387_from_fxsr(fpstate_stub, (void *)®s->fp))
|
|
return -EINVAL;
|
|
#else
|
|
/*
|
|
* On x86_64, we need to sync the FS_BASE/GS_BASE registers using the
|
|
* arch specific data.
|
|
*/
|
|
if (data->arch_data.fs_base != regs->gp[FS_BASE / sizeof(unsigned long)]) {
|
|
data->arch_data.fs_base = regs->gp[FS_BASE / sizeof(unsigned long)];
|
|
data->arch_data.sync |= STUB_SYNC_FS_BASE;
|
|
}
|
|
if (data->arch_data.gs_base != regs->gp[GS_BASE / sizeof(unsigned long)]) {
|
|
data->arch_data.gs_base = regs->gp[GS_BASE / sizeof(unsigned long)];
|
|
data->arch_data.sync |= STUB_SYNC_GS_BASE;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|