Commit 06ee1b68 authored by Roland McGrath's avatar Roland McGrath Committed by Ingo Molnar

x86: x86 ptrace getreg/putreg cleanup

This cleans up the getreg/putreg functions to move the special cases
(segment registers and eflags) out into their own subroutines.
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent e39c2891
...@@ -45,90 +45,120 @@ ...@@ -45,90 +45,120 @@
static long *pt_regs_access(struct pt_regs *regs, unsigned long regno) static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
{ {
BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
regno >>= 2;
if (regno > FS) if (regno > FS)
--regno; --regno;
return &regs->bx + regno; return &regs->bx + regno;
} }
static int putreg(struct task_struct *child, static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
unsigned long regno, unsigned long value)
{ {
struct pt_regs *regs = task_pt_regs(child); /*
regno >>= 2; * Returning the value truncates it to 16 bits.
switch (regno) { */
case GS: unsigned int retval;
if (offset != offsetof(struct user_regs_struct, gs))
retval = *pt_regs_access(task_pt_regs(task), offset);
else {
retval = task->thread.gs;
if (task == current)
savesegment(gs, retval);
}
return retval;
}
static int set_segment_reg(struct task_struct *task,
unsigned long offset, u16 value)
{
/*
* The value argument was already truncated to 16 bits.
*/
if (value && (value & 3) != 3) if (value && (value & 3) != 3)
return -EIO; return -EIO;
child->thread.gs = value;
if (child == current) if (offset != offsetof(struct user_regs_struct, gs))
*pt_regs_access(task_pt_regs(task), offset) = value;
else {
task->thread.gs = value;
if (task == current)
/* /*
* The user-mode %gs is not affected by * The user-mode %gs is not affected by
* kernel entry, so we must update the CPU. * kernel entry, so we must update the CPU.
*/ */
loadsegment(gs, value); loadsegment(gs, value);
}
return 0; return 0;
case DS: }
case ES:
case FS: static unsigned long get_flags(struct task_struct *task)
if (value && (value & 3) != 3) {
return -EIO; unsigned long retval = task_pt_regs(task)->flags;
value &= 0xffff;
break; /*
case SS: * If the debugger set TF, hide it from the readout.
case CS: */
if ((value & 3) != 3) if (test_tsk_thread_flag(task, TIF_FORCED_TF))
return -EIO; retval &= ~X86_EFLAGS_TF;
value &= 0xffff;
break; return retval;
case EFL: }
value &= FLAG_MASK;
static int set_flags(struct task_struct *task, unsigned long value)
{
struct pt_regs *regs = task_pt_regs(task);
/* /*
* If the user value contains TF, mark that * If the user value contains TF, mark that
* it was not "us" (the debugger) that set it. * it was not "us" (the debugger) that set it.
* If not, make sure it stays set if we had. * If not, make sure it stays set if we had.
*/ */
if (value & X86_EFLAGS_TF) if (value & X86_EFLAGS_TF)
clear_tsk_thread_flag(child, TIF_FORCED_TF); clear_tsk_thread_flag(task, TIF_FORCED_TF);
else if (test_tsk_thread_flag(child, TIF_FORCED_TF)) else if (test_tsk_thread_flag(task, TIF_FORCED_TF))
value |= X86_EFLAGS_TF; value |= X86_EFLAGS_TF;
value |= regs->flags & ~FLAG_MASK;
break; regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK);
}
*pt_regs_access(regs, regno) = value;
return 0; return 0;
} }
static unsigned long getreg(struct task_struct *child, unsigned long regno) static int putreg(struct task_struct *child,
unsigned long offset, unsigned long value)
{ {
struct pt_regs *regs = task_pt_regs(child); switch (offset) {
unsigned long retval = ~0UL; case offsetof(struct user_regs_struct, cs):
case offsetof(struct user_regs_struct, ds):
case offsetof(struct user_regs_struct, es):
case offsetof(struct user_regs_struct, fs):
case offsetof(struct user_regs_struct, gs):
case offsetof(struct user_regs_struct, ss):
return set_segment_reg(child, offset, value);
case offsetof(struct user_regs_struct, flags):
return set_flags(child, value);
}
regno >>= 2; *pt_regs_access(task_pt_regs(child), offset) = value;
switch (regno) { return 0;
case EFL: }
/*
* If the debugger set TF, hide it from the readout. static unsigned long getreg(struct task_struct *task, unsigned long offset)
*/ {
retval = regs->flags; switch (offset) {
if (test_tsk_thread_flag(child, TIF_FORCED_TF)) case offsetof(struct user_regs_struct, cs):
retval &= ~X86_EFLAGS_TF; case offsetof(struct user_regs_struct, ds):
break; case offsetof(struct user_regs_struct, es):
case GS: case offsetof(struct user_regs_struct, fs):
retval = child->thread.gs; case offsetof(struct user_regs_struct, gs):
if (child == current) case offsetof(struct user_regs_struct, ss):
savesegment(gs, retval); return get_segment_reg(task, offset);
break;
case DS: case offsetof(struct user_regs_struct, flags):
case ES: return get_flags(task);
case FS:
case SS:
case CS:
retval = 0xffff;
/* fall through */
default:
retval &= *pt_regs_access(regs, regno);
} }
return retval;
return *pt_regs_access(task_pt_regs(task), offset);
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment