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
commit2047b08be67b70875d8765fc81d34ce28041bec3 (patch)
tree04fc804f8530d924283a0c21f4aaafba68070bf7 /arch/x86/kernel/ptrace.c
parent06ee1b687ac91698ccd47fa652d5b3cf1bfcd806 (diff)
x86: x86 ptrace getreg/putreg merge
This merges 64-bit support into the low-level register access functions in arch/x86/kernel/ptrace.c, paving the way to share this file between 32-bit and 64-bit builds. 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.c217
1 files changed, 213 insertions, 4 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index eaec75a4094b..c709868d28a5 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -24,6 +24,8 @@
24#include <asm/debugreg.h> 24#include <asm/debugreg.h>
25#include <asm/ldt.h> 25#include <asm/ldt.h>
26#include <asm/desc.h> 26#include <asm/desc.h>
27#include <asm/prctl.h>
28#include <asm/proto.h>
27 29
28/* 30/*
29 * does not yet catch signals sent when the child dies. 31 * does not yet catch signals sent when the child dies.
@@ -40,6 +42,16 @@
40 X86_EFLAGS_DF | X86_EFLAGS_OF | \ 42 X86_EFLAGS_DF | X86_EFLAGS_OF | \
41 X86_EFLAGS_RF | X86_EFLAGS_AC)) 43 X86_EFLAGS_RF | X86_EFLAGS_AC))
42 44
45/*
46 * Determines whether a value may be installed in a segment register.
47 */
48static inline bool invalid_selector(u16 value)
49{
50 return unlikely(value != 0 && (value & SEGMENT_RPL_MASK) != USER_RPL);
51}
52
53#ifdef CONFIG_X86_32
54
43#define FLAG_MASK FLAG_MASK_32 55#define FLAG_MASK FLAG_MASK_32
44 56
45static long *pt_regs_access(struct pt_regs *regs, unsigned long regno) 57static long *pt_regs_access(struct pt_regs *regs, unsigned long regno)
@@ -73,7 +85,7 @@ static int set_segment_reg(struct task_struct *task,
73 /* 85 /*
74 * The value argument was already truncated to 16 bits. 86 * The value argument was already truncated to 16 bits.
75 */ 87 */
76 if (value && (value & 3) != 3) 88 if (invalid_selector(value))
77 return -EIO; 89 return -EIO;
78 90
79 if (offset != offsetof(struct user_regs_struct, gs)) 91 if (offset != offsetof(struct user_regs_struct, gs))
@@ -91,6 +103,142 @@ static int set_segment_reg(struct task_struct *task,
91 return 0; 103 return 0;
92} 104}
93 105
106static unsigned long debugreg_addr_limit(struct task_struct *task)
107{
108 return TASK_SIZE - 3;
109}
110
111#else /* CONFIG_X86_64 */
112
113#define FLAG_MASK (FLAG_MASK_32 | X86_EFLAGS_NT)
114
115static unsigned long *pt_regs_access(struct pt_regs *regs, unsigned long offset)
116{
117 BUILD_BUG_ON(offsetof(struct pt_regs, r15) != 0);
118 return &regs->r15 + (offset / sizeof(regs->r15));
119}
120
121static u16 get_segment_reg(struct task_struct *task, unsigned long offset)
122{
123 /*
124 * Returning the value truncates it to 16 bits.
125 */
126 unsigned int seg;
127
128 switch (offset) {
129 case offsetof(struct user_regs_struct, fs):
130 if (task == current) {
131 /* Older gas can't assemble movq %?s,%r?? */
132 asm("movl %%fs,%0" : "=r" (seg));
133 return seg;
134 }
135 return task->thread.fsindex;
136 case offsetof(struct user_regs_struct, gs):
137 if (task == current) {
138 asm("movl %%gs,%0" : "=r" (seg));
139 return seg;
140 }
141 return task->thread.gsindex;
142 case offsetof(struct user_regs_struct, ds):
143 if (task == current) {
144 asm("movl %%ds,%0" : "=r" (seg));
145 return seg;
146 }
147 return task->thread.ds;
148 case offsetof(struct user_regs_struct, es):
149 if (task == current) {
150 asm("movl %%es,%0" : "=r" (seg));
151 return seg;
152 }
153 return task->thread.es;
154
155 case offsetof(struct user_regs_struct, cs):
156 case offsetof(struct user_regs_struct, ss):
157 break;
158 }
159 return *pt_regs_access(task_pt_regs(task), offset);
160}
161
162static int set_segment_reg(struct task_struct *task,
163 unsigned long offset, u16 value)
164{
165 /*
166 * The value argument was already truncated to 16 bits.
167 */
168 if (invalid_selector(value))
169 return -EIO;
170
171 switch (offset) {
172 case offsetof(struct user_regs_struct,fs):
173 /*
174 * If this is setting fs as for normal 64-bit use but
175 * setting fs_base has implicitly changed it, leave it.
176 */
177 if ((value == FS_TLS_SEL && task->thread.fsindex == 0 &&
178 task->thread.fs != 0) ||
179 (value == 0 && task->thread.fsindex == FS_TLS_SEL &&
180 task->thread.fs == 0))
181 break;
182 task->thread.fsindex = value;
183 if (task == current)
184 loadsegment(fs, task->thread.fsindex);
185 break;
186 case offsetof(struct user_regs_struct,gs):
187 /*
188 * If this is setting gs as for normal 64-bit use but
189 * setting gs_base has implicitly changed it, leave it.
190 */
191 if ((value == GS_TLS_SEL && task->thread.gsindex == 0 &&
192 task->thread.gs != 0) ||
193 (value == 0 && task->thread.gsindex == GS_TLS_SEL &&
194 task->thread.gs == 0))
195 break;
196 task->thread.gsindex = value;
197 if (task == current)
198 load_gs_index(task->thread.gsindex);
199 break;
200 case offsetof(struct user_regs_struct,ds):
201 task->thread.ds = value;
202 if (task == current)
203 loadsegment(ds, task->thread.ds);
204 break;
205 case offsetof(struct user_regs_struct,es):
206 task->thread.es = value;
207 if (task == current)
208 loadsegment(es, task->thread.es);
209 break;
210
211 /*
212 * Can't actually change these in 64-bit mode.
213 */
214 case offsetof(struct user_regs_struct,cs):
215#ifdef CONFIG_IA32_EMULATION
216 if (test_tsk_thread_flag(task, TIF_IA32))
217 task_pt_regs(task)->cs = value;
218 break;
219#endif
220 case offsetof(struct user_regs_struct,ss):
221#ifdef CONFIG_IA32_EMULATION
222 if (test_tsk_thread_flag(task, TIF_IA32))
223 task_pt_regs(task)->ss = value;
224 break;
225#endif
226 }
227
228 return 0;
229}
230
231static unsigned long debugreg_addr_limit(struct task_struct *task)
232{
233#ifdef CONFIG_IA32_EMULATION
234 if (test_tsk_thread_flag(task, TIF_IA32))
235 return IA32_PAGE_OFFSET - 3;
236#endif
237 return TASK_SIZE64 - 7;
238}
239
240#endif /* CONFIG_X86_32 */
241
94static unsigned long get_flags(struct task_struct *task) 242static unsigned long get_flags(struct task_struct *task)
95{ 243{
96 unsigned long retval = task_pt_regs(task)->flags; 244 unsigned long retval = task_pt_regs(task)->flags;
@@ -137,6 +285,29 @@ static int putreg(struct task_struct *child,
137 285
138 case offsetof(struct user_regs_struct, flags): 286 case offsetof(struct user_regs_struct, flags):
139 return set_flags(child, value); 287 return set_flags(child, value);
288
289#ifdef CONFIG_X86_64
290 case offsetof(struct user_regs_struct,fs_base):
291 if (value >= TASK_SIZE_OF(child))
292 return -EIO;
293 /*
294 * When changing the segment base, use do_arch_prctl
295 * to set either thread.fs or thread.fsindex and the
296 * corresponding GDT slot.
297 */
298 if (child->thread.fs != value)
299 return do_arch_prctl(child, ARCH_SET_FS, value);
300 return 0;
301 case offsetof(struct user_regs_struct,gs_base):
302 /*
303 * Exactly the same here as the %fs handling above.
304 */
305 if (value >= TASK_SIZE_OF(child))
306 return -EIO;
307 if (child->thread.gs != value)
308 return do_arch_prctl(child, ARCH_SET_GS, value);
309 return 0;
310#endif
140 } 311 }
141 312
142 *pt_regs_access(task_pt_regs(child), offset) = value; 313 *pt_regs_access(task_pt_regs(child), offset) = value;
@@ -156,6 +327,37 @@ static unsigned long getreg(struct task_struct *task, unsigned long offset)
156 327
157 case offsetof(struct user_regs_struct, flags): 328 case offsetof(struct user_regs_struct, flags):
158 return get_flags(task); 329 return get_flags(task);
330
331#ifdef CONFIG_X86_64
332 case offsetof(struct user_regs_struct, fs_base): {
333 /*
334 * do_arch_prctl may have used a GDT slot instead of
335 * the MSR. To userland, it appears the same either
336 * way, except the %fs segment selector might not be 0.
337 */
338 unsigned int seg = task->thread.fsindex;
339 if (task->thread.fs != 0)
340 return task->thread.fs;
341 if (task == current)
342 asm("movl %%fs,%0" : "=r" (seg));
343 if (seg != FS_TLS_SEL)
344 return 0;
345 return get_desc_base(&task->thread.tls_array[FS_TLS]);
346 }
347 case offsetof(struct user_regs_struct, gs_base): {
348 /*
349 * Exactly the same here as the %fs handling above.
350 */
351 unsigned int seg = task->thread.gsindex;
352 if (task->thread.gs != 0)
353 return task->thread.gs;
354 if (task == current)
355 asm("movl %%gs,%0" : "=r" (seg));
356 if (seg != GS_TLS_SEL)
357 return 0;
358 return get_desc_base(&task->thread.tls_array[GS_TLS]);
359 }
360#endif
159 } 361 }
160 362
161 return *pt_regs_access(task_pt_regs(task), offset); 363 return *pt_regs_access(task_pt_regs(task), offset);
@@ -187,7 +389,7 @@ static int ptrace_set_debugreg(struct task_struct *child,
187 if (unlikely(n == 4 || n == 5)) 389 if (unlikely(n == 4 || n == 5))
188 return -EIO; 390 return -EIO;
189 391
190 if (n < 4 && unlikely(data >= TASK_SIZE - 3)) 392 if (n < 4 && unlikely(data >= debugreg_addr_limit(child)))
191 return -EIO; 393 return -EIO;
192 394
193 switch (n) { 395 switch (n) {
@@ -197,6 +399,8 @@ static int ptrace_set_debugreg(struct task_struct *child,
197 case 3: child->thread.debugreg3 = data; break; 399 case 3: child->thread.debugreg3 = data; break;
198 400
199 case 6: 401 case 6:
402 if ((data & ~0xffffffffUL) != 0)
403 return -EIO;
200 child->thread.debugreg6 = data; 404 child->thread.debugreg6 = data;
201 break; 405 break;
202 406
@@ -215,7 +419,7 @@ static int ptrace_set_debugreg(struct task_struct *child,
215 * data in the watchpoint case. 419 * data in the watchpoint case.
216 * 420 *
217 * The invalid values are: 421 * The invalid values are:
218 * - LENi == 0x10 (undefined), so mask |= 0x0f00. 422 * - LENi == 0x10 (undefined), so mask |= 0x0f00. [32-bit]
219 * - R/Wi == 0x10 (break on I/O reads or writes), so 423 * - R/Wi == 0x10 (break on I/O reads or writes), so
220 * mask |= 0x4444. 424 * mask |= 0x4444.
221 * - R/Wi == 0x00 && LENi != 0x00, so we have mask |= 425 * - R/Wi == 0x00 && LENi != 0x00, so we have mask |=
@@ -231,9 +435,14 @@ static int ptrace_set_debugreg(struct task_struct *child,
231 * 64-bit kernel), so the x86_64 mask value is 0x5454. 435 * 64-bit kernel), so the x86_64 mask value is 0x5454.
232 * See the AMD manual no. 24593 (AMD64 System Programming) 436 * See the AMD manual no. 24593 (AMD64 System Programming)
233 */ 437 */
438#ifdef CONFIG_X86_32
439#define DR7_MASK 0x5f54
440#else
441#define DR7_MASK 0x5554
442#endif
234 data &= ~DR_CONTROL_RESERVED; 443 data &= ~DR_CONTROL_RESERVED;
235 for (i = 0; i < 4; i++) 444 for (i = 0; i < 4; i++)
236 if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1) 445 if ((DR7_MASK >> ((data >> (16 + 4*i)) & 0xf)) & 1)
237 return -EIO; 446 return -EIO;
238 child->thread.debugreg7 = data; 447 child->thread.debugreg7 = data;
239 if (data) 448 if (data)