diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 13:31:01 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 13:31:01 +0100 |
commit | 06ee1b687ac91698ccd47fa652d5b3cf1bfcd806 (patch) | |
tree | db41e43959a366a5bfa31f23d631402dd2b78298 /arch | |
parent | e39c2891415b3b5c7381ece06bb45b3c7bdd4342 (diff) |
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: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 162 |
1 files changed, 96 insertions, 66 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index b71226d653e..eaec75a4094 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -45,92 +45,122 @@ static long *pt_regs_access(struct pt_regs *regs, unsigned long regno) { BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); + regno >>= 2; if (regno > FS) --regno; return ®s->bx + regno; } -static int putreg(struct task_struct *child, - unsigned long regno, unsigned long value) +static u16 get_segment_reg(struct task_struct *task, unsigned long offset) { - struct pt_regs *regs = task_pt_regs(child); - regno >>= 2; - switch (regno) { - case GS: - if (value && (value & 3) != 3) - return -EIO; - child->thread.gs = value; - if (child == current) + /* + * Returning the value truncates it to 16 bits. + */ + 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) + return -EIO; + + 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 * kernel entry, so we must update the CPU. */ loadsegment(gs, value); - return 0; - case DS: - case ES: - case FS: - if (value && (value & 3) != 3) - return -EIO; - value &= 0xffff; - break; - case SS: - case CS: - if ((value & 3) != 3) - return -EIO; - value &= 0xffff; - break; - case EFL: - value &= FLAG_MASK; - /* - * If the user value contains TF, mark that - * it was not "us" (the debugger) that set it. - * If not, make sure it stays set if we had. - */ - if (value & X86_EFLAGS_TF) - clear_tsk_thread_flag(child, TIF_FORCED_TF); - else if (test_tsk_thread_flag(child, TIF_FORCED_TF)) - value |= X86_EFLAGS_TF; - value |= regs->flags & ~FLAG_MASK; - break; } - *pt_regs_access(regs, regno) = value; + return 0; } -static unsigned long getreg(struct task_struct *child, unsigned long regno) +static unsigned long get_flags(struct task_struct *task) { - struct pt_regs *regs = task_pt_regs(child); - unsigned long retval = ~0UL; + unsigned long retval = task_pt_regs(task)->flags; + + /* + * If the debugger set TF, hide it from the readout. + */ + if (test_tsk_thread_flag(task, TIF_FORCED_TF)) + retval &= ~X86_EFLAGS_TF; - regno >>= 2; - switch (regno) { - case EFL: - /* - * If the debugger set TF, hide it from the readout. - */ - retval = regs->flags; - if (test_tsk_thread_flag(child, TIF_FORCED_TF)) - retval &= ~X86_EFLAGS_TF; - break; - case GS: - retval = child->thread.gs; - if (child == current) - savesegment(gs, retval); - break; - case DS: - case ES: - case FS: - case SS: - case CS: - retval = 0xffff; - /* fall through */ - default: - retval &= *pt_regs_access(regs, regno); - } return retval; } +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 + * it was not "us" (the debugger) that set it. + * If not, make sure it stays set if we had. + */ + if (value & X86_EFLAGS_TF) + clear_tsk_thread_flag(task, TIF_FORCED_TF); + else if (test_tsk_thread_flag(task, TIF_FORCED_TF)) + value |= X86_EFLAGS_TF; + + regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK); + + return 0; +} + +static int putreg(struct task_struct *child, + unsigned long offset, unsigned long value) +{ + switch (offset) { + 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); + } + + *pt_regs_access(task_pt_regs(child), offset) = value; + return 0; +} + +static unsigned long getreg(struct task_struct *task, unsigned long offset) +{ + switch (offset) { + 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 get_segment_reg(task, offset); + + case offsetof(struct user_regs_struct, flags): + return get_flags(task); + } + + return *pt_regs_access(task_pt_regs(task), offset); +} + /* * This function is trivial and will be inlined by the compiler. * Having it separates the implementation details of debug |