aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/step.c
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2008-01-30 07:30:50 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:30:50 -0500
commitfa1e03eae2f38e7b38095301b043da9c274d2284 (patch)
tree60503087ebaaabf1e4fd62b51a93fce411e2b8da /arch/x86/kernel/step.c
parent7f232343e0ea37ffc0a552cdbd4825482c949281 (diff)
x86: single_step moved
This moves the single-step support code from ptrace_64.c into a new file step.c, verbatim. This paves the way for consolidating this code between 64-bit and 32-bit versions. 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/step.c')
-rw-r--r--arch/x86/kernel/step.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c
new file mode 100644
index 000000000000..cb3c8bc2939a
--- /dev/null
+++ b/arch/x86/kernel/step.c
@@ -0,0 +1,140 @@
1/*
2 * x86 single-step support code, common to 32-bit and 64-bit.
3 */
4#include <linux/sched.h>
5#include <linux/mm.h>
6#include <linux/ptrace.h>
7
8#define LDT_SEGMENT 4
9
10unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs)
11{
12 unsigned long addr, seg;
13
14 addr = regs->rip;
15 seg = regs->cs & 0xffff;
16
17 /*
18 * We'll assume that the code segments in the GDT
19 * are all zero-based. That is largely true: the
20 * TLS segments are used for data, and the PNPBIOS
21 * and APM bios ones we just ignore here.
22 */
23 if (seg & LDT_SEGMENT) {
24 u32 *desc;
25 unsigned long base;
26
27 seg &= ~7UL;
28
29 mutex_lock(&child->mm->context.lock);
30 if (unlikely((seg >> 3) >= child->mm->context.size))
31 addr = -1L; /* bogus selector, access would fault */
32 else {
33 desc = child->mm->context.ldt + seg;
34 base = ((desc[0] >> 16) |
35 ((desc[1] & 0xff) << 16) |
36 (desc[1] & 0xff000000));
37
38 /* 16-bit code segment? */
39 if (!((desc[1] >> 22) & 1))
40 addr &= 0xffff;
41 addr += base;
42 }
43 mutex_unlock(&child->mm->context.lock);
44 }
45
46 return addr;
47}
48
49static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
50{
51 int i, copied;
52 unsigned char opcode[15];
53 unsigned long addr = convert_rip_to_linear(child, regs);
54
55 copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
56 for (i = 0; i < copied; i++) {
57 switch (opcode[i]) {
58 /* popf and iret */
59 case 0x9d: case 0xcf:
60 return 1;
61
62 /* CHECKME: 64 65 */
63
64 /* opcode and address size prefixes */
65 case 0x66: case 0x67:
66 continue;
67 /* irrelevant prefixes (segment overrides and repeats) */
68 case 0x26: case 0x2e:
69 case 0x36: case 0x3e:
70 case 0x64: case 0x65:
71 case 0xf2: case 0xf3:
72 continue;
73
74 case 0x40 ... 0x4f:
75 if (regs->cs != __USER_CS)
76 /* 32-bit mode: register increment */
77 return 0;
78 /* 64-bit mode: REX prefix */
79 continue;
80
81 /* CHECKME: f2, f3 */
82
83 /*
84 * pushf: NOTE! We should probably not let
85 * the user see the TF bit being set. But
86 * it's more pain than it's worth to avoid
87 * it, and a debugger could emulate this
88 * all in user space if it _really_ cares.
89 */
90 case 0x9c:
91 default:
92 return 0;
93 }
94 }
95 return 0;
96}
97
98void user_enable_single_step(struct task_struct *child)
99{
100 struct pt_regs *regs = task_pt_regs(child);
101
102 /*
103 * Always set TIF_SINGLESTEP - this guarantees that
104 * we single-step system calls etc.. This will also
105 * cause us to set TF when returning to user mode.
106 */
107 set_tsk_thread_flag(child, TIF_SINGLESTEP);
108
109 /*
110 * If TF was already set, don't do anything else
111 */
112 if (regs->eflags & X86_EFLAGS_TF)
113 return;
114
115 /* Set TF on the kernel stack.. */
116 regs->eflags |= X86_EFLAGS_TF;
117
118 /*
119 * ..but if TF is changed by the instruction we will trace,
120 * don't mark it as being "us" that set it, so that we
121 * won't clear it by hand later.
122 */
123 if (is_setting_trap_flag(child, regs))
124 return;
125
126 child->ptrace |= PT_DTRACE;
127}
128
129void user_disable_single_step(struct task_struct *child)
130{
131 /* Always clear TIF_SINGLESTEP... */
132 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
133
134 /* But touch TF only if it was set by us.. */
135 if (child->ptrace & PT_DTRACE) {
136 struct pt_regs *regs = task_pt_regs(child);
137 regs->eflags &= ~X86_EFLAGS_TF;
138 child->ptrace &= ~PT_DTRACE;
139 }
140}