aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/ptrace.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-01-30 07:31:01 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:31:01 -0500
commit06ee1b687ac91698ccd47fa652d5b3cf1bfcd806 (patch)
treedb41e43959a366a5bfa31f23d631402dd2b78298 /arch/x86/kernel/ptrace.c
parente39c2891415b3b5c7381ece06bb45b3c7bdd4342 (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/x86/kernel/ptrace.c')
-rw-r--r--arch/x86/kernel/ptrace.c162
1 files changed, 96 insertions, 66 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index b71226d653ed..eaec75a4094b 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -45,92 +45,122 @@
45static long *pt_regs_access(struct pt_regs *regs, unsigned long regno) 45static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
46{ 46{
47 BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0); 47 BUILD_BUG_ON(offsetof(struct pt_regs, bx) != 0);
48 regno >>= 2;
48 if (regno > FS) 49 if (regno > FS)
49 --regno; 50 --regno;
50 return &regs->bx + regno; 51 return &regs->bx + regno;
51} 52}
52 53
53static int putreg(struct task_struct *child, 54static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
54 unsigned long regno, unsigned long value)
55{ 55{
56 struct pt_regs *regs = task_pt_regs(child); 56 /*
57 regno >>= 2; 57 * Returning the value truncates it to 16 bits.
58 switch (regno) { 58 */
59 case GS: 59 unsigned int retval;
60 if (value && (value & 3) != 3) 60 if (offset != offsetof(struct user_regs_struct, gs))
61 return -EIO; 61 retval = *pt_regs_access(task_pt_regs(task), offset);
62 child->thread.gs = value; 62 else {
63 if (child == current) 63 retval = task->thread.gs;
64 if (task == current)
65 savesegment(gs, retval);
66 }
67 return retval;
68}
69
70static int set_segment_reg(struct task_struct *task,
71 unsigned long offset, u16 value)
72{
73 /*
74 * The value argument was already truncated to 16 bits.
75 */
76 if (value && (value & 3) != 3)
77 return -EIO;
78
79 if (offset != offsetof(struct user_regs_struct, gs))
80 *pt_regs_access(task_pt_regs(task), offset) = value;
81 else {
82 task->thread.gs = value;
83 if (task == current)
64 /* 84 /*
65 * The user-mode %gs is not affected by 85 * The user-mode %gs is not affected by
66 * kernel entry, so we must update the CPU. 86 * kernel entry, so we must update the CPU.
67 */ 87 */
68 loadsegment(gs, value); 88 loadsegment(gs, value);
69 return 0;
70 case DS:
71 case ES:
72 case FS:
73 if (value && (value & 3) != 3)
74 return -EIO;
75 value &= 0xffff;
76 break;
77 case SS:
78 case CS:
79 if ((value & 3) != 3)
80 return -EIO;
81 value &= 0xffff;
82 break;
83 case EFL:
84 value &= FLAG_MASK;
85 /*
86 * If the user value contains TF, mark that
87 * it was not "us" (the debugger) that set it.
88 * If not, make sure it stays set if we had.
89 */
90 if (value & X86_EFLAGS_TF)
91 clear_tsk_thread_flag(child, TIF_FORCED_TF);
92 else if (test_tsk_thread_flag(child, TIF_FORCED_TF))
93 value |= X86_EFLAGS_TF;
94 value |= regs->flags & ~FLAG_MASK;
95 break;
96 } 89 }
97 *pt_regs_access(regs, regno) = value; 90
98 return 0; 91 return 0;
99} 92}
100 93
101static unsigned long getreg(struct task_struct *child, unsigned long regno) 94static unsigned long get_flags(struct task_struct *task)
102{ 95{
103 struct pt_regs *regs = task_pt_regs(child); 96 unsigned long retval = task_pt_regs(task)->flags;
104 unsigned long retval = ~0UL; 97
98 /*
99 * If the debugger set TF, hide it from the readout.
100 */
101 if (test_tsk_thread_flag(task, TIF_FORCED_TF))
102 retval &= ~X86_EFLAGS_TF;
105 103
106 regno >>= 2;
107 switch (regno) {
108 case EFL:
109 /*
110 * If the debugger set TF, hide it from the readout.
111 */
112 retval = regs->flags;
113 if (test_tsk_thread_flag(child, TIF_FORCED_TF))
114 retval &= ~X86_EFLAGS_TF;
115 break;
116 case GS:
117 retval = child->thread.gs;
118 if (child == current)
119 savesegment(gs, retval);
120 break;
121 case DS:
122 case ES:
123 case FS:
124 case SS:
125 case CS:
126 retval = 0xffff;
127 /* fall through */
128 default:
129 retval &= *pt_regs_access(regs, regno);
130 }
131 return retval; 104 return retval;
132} 105}
133 106
107static int set_flags(struct task_struct *task, unsigned long value)
108{
109 struct pt_regs *regs = task_pt_regs(task);
110
111 /*
112 * If the user value contains TF, mark that
113 * it was not "us" (the debugger) that set it.
114 * If not, make sure it stays set if we had.
115 */
116 if (value & X86_EFLAGS_TF)
117 clear_tsk_thread_flag(task, TIF_FORCED_TF);
118 else if (test_tsk_thread_flag(task, TIF_FORCED_TF))
119 value |= X86_EFLAGS_TF;
120
121 regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK);
122
123 return 0;
124}
125
126static int putreg(struct task_struct *child,
127 unsigned long offset, unsigned long value)
128{
129 switch (offset) {
130 case offsetof(struct user_regs_struct, cs):
131 case offsetof(struct user_regs_struct, ds):
132 case offsetof(struct user_regs_struct, es):
133 case offsetof(struct user_regs_struct, fs):
134 case offsetof(struct user_regs_struct, gs):
135 case offsetof(struct user_regs_struct, ss):
136 return set_segment_reg(child, offset, value);
137
138 case offsetof(struct user_regs_struct, flags):
139 return set_flags(child, value);
140 }
141
142 *pt_regs_access(task_pt_regs(child), offset) = value;
143 return 0;
144}
145
146static unsigned long getreg(struct task_struct *task, unsigned long offset)
147{
148 switch (offset) {
149 case offsetof(struct user_regs_struct, cs):
150 case offsetof(struct user_regs_struct, ds):
151 case offsetof(struct user_regs_struct, es):
152 case offsetof(struct user_regs_struct, fs):
153 case offsetof(struct user_regs_struct, gs):
154 case offsetof(struct user_regs_struct, ss):
155 return get_segment_reg(task, offset);
156
157 case offsetof(struct user_regs_struct, flags):
158 return get_flags(task);
159 }
160
161 return *pt_regs_access(task_pt_regs(task), offset);
162}
163
134/* 164/*
135 * This function is trivial and will be inlined by the compiler. 165 * This function is trivial and will be inlined by the compiler.
136 * Having it separates the implementation details of debug 166 * Having it separates the implementation details of debug