step.c 4.8 KB
Newer Older
R
Roland McGrath 已提交
1 2 3 4 5 6 7
/*
 * x86 single-step support code, common to 32-bit and 64-bit.
 */
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/ptrace.h>

H
Harvey Harrison 已提交
8
unsigned long convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs)
R
Roland McGrath 已提交
9 10 11
{
	unsigned long addr, seg;

12
	addr = regs->ip;
R
Roland McGrath 已提交
13
	seg = regs->cs & 0xffff;
14
	if (v8086_mode(regs)) {
R
Roland McGrath 已提交
15 16 17
		addr = (addr & 0xffff) + (seg << 4);
		return addr;
	}
R
Roland McGrath 已提交
18 19 20 21 22 23 24

	/*
	 * We'll assume that the code segments in the GDT
	 * are all zero-based. That is largely true: the
	 * TLS segments are used for data, and the PNPBIOS
	 * and APM bios ones we just ignore here.
	 */
R
Roland McGrath 已提交
25
	if ((seg & SEGMENT_TI_MASK) == SEGMENT_LDT) {
R
Roland McGrath 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
		u32 *desc;
		unsigned long base;

		seg &= ~7UL;

		mutex_lock(&child->mm->context.lock);
		if (unlikely((seg >> 3) >= child->mm->context.size))
			addr = -1L; /* bogus selector, access would fault */
		else {
			desc = child->mm->context.ldt + seg;
			base = ((desc[0] >> 16) |
				((desc[1] & 0xff) << 16) |
				(desc[1] & 0xff000000));

			/* 16-bit code segment? */
			if (!((desc[1] >> 22) & 1))
				addr &= 0xffff;
			addr += base;
		}
		mutex_unlock(&child->mm->context.lock);
	}

	return addr;
}

static int is_setting_trap_flag(struct task_struct *child, struct pt_regs *regs)
{
	int i, copied;
	unsigned char opcode[15];
H
Harvey Harrison 已提交
55
	unsigned long addr = convert_ip_to_linear(child, regs);
R
Roland McGrath 已提交
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

	copied = access_process_vm(child, addr, opcode, sizeof(opcode), 0);
	for (i = 0; i < copied; i++) {
		switch (opcode[i]) {
		/* popf and iret */
		case 0x9d: case 0xcf:
			return 1;

			/* CHECKME: 64 65 */

		/* opcode and address size prefixes */
		case 0x66: case 0x67:
			continue;
		/* irrelevant prefixes (segment overrides and repeats) */
		case 0x26: case 0x2e:
		case 0x36: case 0x3e:
		case 0x64: case 0x65:
R
Roland McGrath 已提交
73
		case 0xf0: case 0xf2: case 0xf3:
R
Roland McGrath 已提交
74 75
			continue;

R
Roland McGrath 已提交
76
#ifdef CONFIG_X86_64
R
Roland McGrath 已提交
77 78 79 80 81 82
		case 0x40 ... 0x4f:
			if (regs->cs != __USER_CS)
				/* 32-bit mode: register increment */
				return 0;
			/* 64-bit mode: REX prefix */
			continue;
R
Roland McGrath 已提交
83
#endif
R
Roland McGrath 已提交
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

			/* CHECKME: f2, f3 */

		/*
		 * pushf: NOTE! We should probably not let
		 * the user see the TF bit being set. But
		 * it's more pain than it's worth to avoid
		 * it, and a debugger could emulate this
		 * all in user space if it _really_ cares.
		 */
		case 0x9c:
		default:
			return 0;
		}
	}
	return 0;
}

102 103 104 105
/*
 * Enable single-stepping.  Return nonzero if user mode is not using TF itself.
 */
static int enable_single_step(struct task_struct *child)
R
Roland McGrath 已提交
106 107 108 109 110 111 112 113 114 115 116 117 118
{
	struct pt_regs *regs = task_pt_regs(child);

	/*
	 * Always set TIF_SINGLESTEP - this guarantees that
	 * we single-step system calls etc..  This will also
	 * cause us to set TF when returning to user mode.
	 */
	set_tsk_thread_flag(child, TIF_SINGLESTEP);

	/*
	 * If TF was already set, don't do anything else
	 */
119
	if (regs->flags & X86_EFLAGS_TF)
120
		return 0;
R
Roland McGrath 已提交
121 122

	/* Set TF on the kernel stack.. */
123
	regs->flags |= X86_EFLAGS_TF;
R
Roland McGrath 已提交
124 125 126 127 128 129 130

	/*
	 * ..but if TF is changed by the instruction we will trace,
	 * don't mark it as being "us" that set it, so that we
	 * won't clear it by hand later.
	 */
	if (is_setting_trap_flag(child, regs))
131
		return 0;
R
Roland McGrath 已提交
132

R
Roland McGrath 已提交
133
	set_tsk_thread_flag(child, TIF_FORCED_TF);
134 135 136 137 138 139 140 141 142

	return 1;
}

/*
 * Install this value in MSR_IA32_DEBUGCTLMSR whenever child is running.
 */
static void write_debugctlmsr(struct task_struct *child, unsigned long val)
{
143 144 145
	if (child->thread.debugctlmsr == val)
		return;

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
	child->thread.debugctlmsr = val;

	if (child != current)
		return;

	wrmsrl(MSR_IA32_DEBUGCTLMSR, val);
}

/*
 * Enable single or block step.
 */
static void enable_step(struct task_struct *child, bool block)
{
	/*
	 * Make sure block stepping (BTF) is not enabled unless it should be.
	 * Note that we don't try to worry about any is_setting_trap_flag()
	 * instructions after the first when using block stepping.
	 * So noone should try to use debugger block stepping in a program
	 * that uses user-mode single stepping itself.
	 */
	if (enable_single_step(child) && block) {
		set_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
168 169 170
		write_debugctlmsr(child,
				  child->thread.debugctlmsr | DEBUGCTLMSR_BTF);
	} else {
171 172
		write_debugctlmsr(child,
				  child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF);
173

174 175
		if (!child->thread.debugctlmsr)
			clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
176 177 178 179 180 181 182 183 184 185 186
	}
}

void user_enable_single_step(struct task_struct *child)
{
	enable_step(child, 0);
}

void user_enable_block_step(struct task_struct *child)
{
	enable_step(child, 1);
R
Roland McGrath 已提交
187 188 189 190
}

void user_disable_single_step(struct task_struct *child)
{
191 192 193
	/*
	 * Make sure block stepping (BTF) is disabled.
	 */
194
	write_debugctlmsr(child,
J
Jan Beulich 已提交
195
			  child->thread.debugctlmsr & ~DEBUGCTLMSR_BTF);
196 197 198

	if (!child->thread.debugctlmsr)
		clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR);
199

R
Roland McGrath 已提交
200 201 202 203
	/* Always clear TIF_SINGLESTEP... */
	clear_tsk_thread_flag(child, TIF_SINGLESTEP);

	/* But touch TF only if it was set by us.. */
R
Roland McGrath 已提交
204
	if (test_and_clear_tsk_thread_flag(child, TIF_FORCED_TF))
205
		task_pt_regs(child)->flags &= ~X86_EFLAGS_TF;
R
Roland McGrath 已提交
206
}