Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 962ff380 authored by Roland McGrath's avatar Roland McGrath Committed by Ingo Molnar
Browse files

x86: x86-64 ptrace debugreg cleanup



This cleans up the 64-bit ptrace code to separate the guts of the
debug register access from the implementation of PTRACE_PEEKUSR and
PTRACE_POKEUSR.  The new functions ptrace_[gs]et_debugreg are made
global so that the ia32 code can later be changed to call them too.

Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent e4aed6cc
Loading
Loading
Loading
Loading
+66 −74
Original line number Diff line number Diff line
@@ -183,9 +183,63 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno)

}

unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
{
	switch (n) {
	case 0:		return child->thread.debugreg0;
	case 1:		return child->thread.debugreg1;
	case 2:		return child->thread.debugreg2;
	case 3:		return child->thread.debugreg3;
	case 6:		return child->thread.debugreg6;
	case 7:		return child->thread.debugreg7;
	}
	return 0;
}

int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long data)
{
	int i;

	if (n < 4) {
		int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7;
		if (unlikely(data >= TASK_SIZE_OF(child) - dsize))
			return -EIO;
	}

	switch (n) {
	case 0:		child->thread.debugreg0 = data; break;
	case 1:		child->thread.debugreg1 = data; break;
	case 2:		child->thread.debugreg2 = data; break;
	case 3:		child->thread.debugreg3 = data; break;

	case 6:
		if (data >> 32)
			return -EIO;
		child->thread.debugreg6 = data;
		break;

	case 7:
		/*
		 * See ptrace_32.c for an explanation of this awkward check.
		 */
		data &= ~DR_CONTROL_RESERVED;
		for (i = 0; i < 4; i++)
			if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
				return -EIO;
		child->thread.debugreg7 = data;
		if (data)
			set_tsk_thread_flag(child, TIF_DEBUG);
		else
			clear_tsk_thread_flag(child, TIF_DEBUG);
		break;
	}

	return 0;
}

long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
	long i, ret;
	long ret;
	unsigned ui;

	switch (request) {
@@ -204,32 +258,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
		    addr > sizeof(struct user) - 7)
			break;

		switch (addr) { 
		case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
			tmp = getreg(child, addr);
			break;
		case offsetof(struct user, u_debugreg[0]):
			tmp = child->thread.debugreg0;
			break;
		case offsetof(struct user, u_debugreg[1]):
			tmp = child->thread.debugreg1;
			break;
		case offsetof(struct user, u_debugreg[2]):
			tmp = child->thread.debugreg2;
			break;
		case offsetof(struct user, u_debugreg[3]):
			tmp = child->thread.debugreg3;
			break;
		case offsetof(struct user, u_debugreg[6]):
			tmp = child->thread.debugreg6;
			break;
		case offsetof(struct user, u_debugreg[7]):
			tmp = child->thread.debugreg7;
			break;
		default:
		tmp = 0;
			break;
		if (addr < sizeof(struct user_regs_struct))
			tmp = getreg(child, addr);
		else if (addr >= offsetof(struct user, u_debugreg[0])) {
			addr -= offsetof(struct user, u_debugreg[0]);
			tmp = ptrace_get_debugreg(child, addr / sizeof(long));
		}

		ret = put_user(tmp,(unsigned long __user *) data);
		break;
	}
@@ -241,63 +277,19 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
		break;

	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
	{
		int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7;
		ret = -EIO;
		if ((addr & 7) ||
		    addr > sizeof(struct user) - 7)
			break;

		switch (addr) { 
		case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
		if (addr < sizeof(struct user_regs_struct))
			ret = putreg(child, addr, data);
			break;
		/* Disallows to set a breakpoint into the vsyscall */
		case offsetof(struct user, u_debugreg[0]):
			if (data >= TASK_SIZE_OF(child) - dsize) break;
			child->thread.debugreg0 = data;
			ret = 0;
			break;
		case offsetof(struct user, u_debugreg[1]):
			if (data >= TASK_SIZE_OF(child) - dsize) break;
			child->thread.debugreg1 = data;
			ret = 0;
			break;
		case offsetof(struct user, u_debugreg[2]):
			if (data >= TASK_SIZE_OF(child) - dsize) break;
			child->thread.debugreg2 = data;
			ret = 0;
			break;
		case offsetof(struct user, u_debugreg[3]):
			if (data >= TASK_SIZE_OF(child) - dsize) break;
			child->thread.debugreg3 = data;
			ret = 0;
			break;
		case offsetof(struct user, u_debugreg[6]):
				  if (data >> 32)
				break; 
			child->thread.debugreg6 = data;
			ret = 0;
			break;
		case offsetof(struct user, u_debugreg[7]):
			/* See arch/i386/kernel/ptrace.c for an explanation of
			 * this awkward check.*/
			data &= ~DR_CONTROL_RESERVED;
			for(i=0; i<4; i++)
				if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
					break;
			if (i == 4) {
			  child->thread.debugreg7 = data;
			  if (data)
			  	set_tsk_thread_flag(child, TIF_DEBUG);
			  else
			  	clear_tsk_thread_flag(child, TIF_DEBUG);
			  ret = 0;
		  	}
		  break;
		else if (addr >= offsetof(struct user, u_debugreg[0])) {
			addr -= offsetof(struct user, u_debugreg[0]);
			ret = ptrace_set_debugreg(child,
						  addr / sizeof(long), data);
		}
		break;
	}

#ifdef CONFIG_IA32_EMULATION
		/* This makes only sense with 32bit programs. Allow a
+3 −0
Original line number Diff line number Diff line
@@ -110,6 +110,9 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where);

struct task_struct;

extern unsigned long ptrace_get_debugreg(struct task_struct *child, int n);
extern int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long);

extern unsigned long
convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs);