aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--arch/x86/kernel/Makefile_642
-rw-r--r--arch/x86/kernel/ptrace_64.c134
-rw-r--r--arch/x86/kernel/step.c140
3 files changed, 142 insertions, 134 deletions
diff --git a/arch/x86/kernel/Makefile_64 b/arch/x86/kernel/Makefile_64
index e1ba82e582a8..d908f0175e76 100644
--- a/arch/x86/kernel/Makefile_64
+++ b/arch/x86/kernel/Makefile_64
@@ -13,6 +13,8 @@ obj-y := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \
13 pci-dma_64.o pci-nommu_64.o alternative.o hpet.o tsc_64.o bugs_64.o \ 13 pci-dma_64.o pci-nommu_64.o alternative.o hpet.o tsc_64.o bugs_64.o \
14 i8253.o io_delay.o rtc.o 14 i8253.o io_delay.o rtc.o
15 15
16obj-y += step.o
17
16obj-$(CONFIG_IA32_EMULATION) += tls.o 18obj-$(CONFIG_IA32_EMULATION) += tls.o
17obj-$(CONFIG_STACKTRACE) += stacktrace.o 19obj-$(CONFIG_STACKTRACE) += stacktrace.o
18obj-y += cpu/ 20obj-y += cpu/
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c
index 7373a99facf3..4abfbced9b26 100644
--- a/arch/x86/kernel/ptrace_64.c
+++ b/arch/x86/kernel/ptrace_64.c
@@ -80,140 +80,6 @@ static inline long put_stack_long(struct task_struct *task, int offset,
80 return 0; 80 return 0;
81} 81}
82 82
83#define LDT_SEGMENT 4
84
85unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs)
86{
87 unsigned long addr, seg;
88
89 addr = regs->rip;
90 seg = regs->cs & 0xffff;
91
92 /*
93 * We'll assume that the code segments in the GDT
94 * are all zero-based. That is largely true: the
95 * TLS segments are used for data, and the PNPBIOS
96 * and APM bios ones we just ignore here.
97 */
98 if (seg & LDT_SEGMENT) {
99 u32 *desc;
100 unsigned long base;
101
102 seg &= ~7UL;
103
104 mutex_lock(&child->mm->context.lock);
105 if (unlikely((seg >> 3) >= child->mm->context.size))
106 addr = -1L; /* bogus selector, access would fault */
107 else {
108 desc = child->mm->context.ldt + seg;
109 base = ((desc[0] >> 16) |
110 ((desc[1] & 0xff) << 16) |
111 (desc[1] & 0xff000000));
112
113 /* 16-bit code segment? */
114 if (!((desc[1] >> 22) & 1))
115 addr &= 0xffff;
116 addr += base;
117 }
118 mutex_unlock(&child->mm->context.lock);
119 }
120
121 return addr;
122}
123
124static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
125{
126 int i, copied;
127 unsigned char opcode[15];
128 unsigned long addr = convert_rip_to_linear(child, regs);
129
130 copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
131 for (i = 0; i < copied; i++) {
132 switch (opcode[i]) {
133 /* popf and iret */
134 case 0x9d: case 0xcf:
135 return 1;
136
137 /* CHECKME: 64 65 */
138
139 /* opcode and address size prefixes */
140 case 0x66: case 0x67:
141 continue;
142 /* irrelevant prefixes (segment overrides and repeats) */
143 case 0x26: case 0x2e:
144 case 0x36: case 0x3e:
145 case 0x64: case 0x65:
146 case 0xf2: case 0xf3:
147 continue;
148
149 case 0x40 ... 0x4f:
150 if (regs->cs != __USER_CS)
151 /* 32-bit mode: register increment */
152 return 0;
153 /* 64-bit mode: REX prefix */
154 continue;
155
156 /* CHECKME: f2, f3 */
157
158 /*
159 * pushf: NOTE! We should probably not let
160 * the user see the TF bit being set. But
161 * it's more pain than it's worth to avoid
162 * it, and a debugger could emulate this
163 * all in user space if it _really_ cares.
164 */
165 case 0x9c:
166 default:
167 return 0;
168 }
169 }
170 return 0;
171}
172
173void user_enable_single_step(struct task_struct *child)
174{
175 struct pt_regs *regs = task_pt_regs(child);
176
177 /*
178 * Always set TIF_SINGLESTEP - this guarantees that
179 * we single-step system calls etc.. This will also
180 * cause us to set TF when returning to user mode.
181 */
182 set_tsk_thread_flag(child, TIF_SINGLESTEP);
183
184 /*
185 * If TF was already set, don't do anything else
186 */
187 if (regs->eflags & X86_EFLAGS_TF)
188 return;
189
190 /* Set TF on the kernel stack.. */
191 regs->eflags |= X86_EFLAGS_TF;
192
193 /*
194 * ..but if TF is changed by the instruction we will trace,
195 * don't mark it as being "us" that set it, so that we
196 * won't clear it by hand later.
197 */
198 if (is_setting_trap_flag(child, regs))
199 return;
200
201 child->ptrace |= PT_DTRACE;
202}
203
204void user_disable_single_step(struct task_struct *child)
205{
206 /* Always clear TIF_SINGLESTEP... */
207 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
208
209 /* But touch TF only if it was set by us.. */
210 if (child->ptrace & PT_DTRACE) {
211 struct pt_regs *regs = task_pt_regs(child);
212 regs->eflags &= ~X86_EFLAGS_TF;
213 child->ptrace &= ~PT_DTRACE;
214 }
215}
216
217/* 83/*
218 * Called by kernel/ptrace.c when detaching.. 84 * Called by kernel/ptrace.c when detaching..
219 * 85 *
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}