Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 79909
b: refs/heads/master
c: 06ee1b6
h: refs/heads/master
i:
  79907: 52306cc
v: v3
  • Loading branch information
Roland McGrath authored and Ingo Molnar committed Jan 30, 2008
1 parent 33cf397 commit 30a26d7
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 67 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e39c2891415b3b5c7381ece06bb45b3c7bdd4342
refs/heads/master: 06ee1b687ac91698ccd47fa652d5b3cf1bfcd806
162 changes: 96 additions & 66 deletions trunk/arch/x86/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 &regs->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
Expand Down

0 comments on commit 30a26d7

Please sign in to comment.