ptrace.c 3.2 KB
Newer Older
A
Aurelien Jacquiot 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
/*
 *  Port on Texas Instruments TMS320C6x architecture
 *
 *  Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated
 *  Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
 *
 *  Updated for 2.6.34: Mark Salter <msalter@redhat.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 */
#include <linux/ptrace.h>
#include <linux/tracehook.h>
#include <linux/regset.h>
#include <linux/elf.h>

#include <asm/cacheflush.h>

#define PT_REG_SIZE	  (sizeof(struct pt_regs))

/*
 * Called by kernel/ptrace.c when detaching.
 */
void ptrace_disable(struct task_struct *child)
{
	/* nothing to do */
}

/*
 * Get a register number from live pt_regs for the specified task.
 */
static inline long get_reg(struct task_struct *task, int regno)
{
	long *addr = (long *)task_pt_regs(task);

	if (regno == PT_TSR || regno == PT_CSR)
		return 0;

	return addr[regno];
}

/*
 * Write contents of register REGNO in task TASK.
 */
static inline int put_reg(struct task_struct *task,
			  int regno,
			  unsigned long data)
{
	unsigned long *addr = (unsigned long *)task_pt_regs(task);

	if (regno != PT_TSR && regno != PT_CSR)
		addr[regno] = data;

	return 0;
}

/* regset get/set implementations */

static int gpr_get(struct task_struct *target,
		   const struct user_regset *regset,
		   unsigned int pos, unsigned int count,
		   void *kbuf, void __user *ubuf)
{
	struct pt_regs *regs = task_pt_regs(target);

	return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
				   regs,
				   0, sizeof(*regs));
}

enum c6x_regset {
	REGSET_GPR,
};

static const struct user_regset c6x_regsets[] = {
	[REGSET_GPR] = {
		.core_note_type = NT_PRSTATUS,
		.n = ELF_NGREG,
		.size = sizeof(u32),
		.align = sizeof(u32),
		.get = gpr_get,
	},
};

static const struct user_regset_view user_c6x_native_view = {
	.name		= "tic6x",
	.e_machine	= EM_TI_C6000,
	.regsets	= c6x_regsets,
	.n		= ARRAY_SIZE(c6x_regsets),
};

const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
	return &user_c6x_native_view;
}

/*
 * Perform ptrace request
 */
long arch_ptrace(struct task_struct *child, long request,
		 unsigned long addr, unsigned long data)
{
	int ret = 0;

	switch (request) {
		/*
		 * write the word at location addr.
		 */
	case PTRACE_POKETEXT:
		ret = generic_ptrace_pokedata(child, addr, data);
		if (ret == 0 && request == PTRACE_POKETEXT)
			flush_icache_range(addr, addr + 4);
		break;
	default:
		ret = ptrace_request(child, request, addr, data);
		break;
	}

	return ret;
}

/*
 * handle tracing of system call entry
 * - return the revised system call number or ULONG_MAX to cause ENOSYS
 */
asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs)
{
	if (tracehook_report_syscall_entry(regs))
		/* tracing decided this syscall should not happen, so
		 * We'll return a bogus call number to get an ENOSYS
		 * error, but leave the original number in
		 * regs->orig_a4
		 */
		return ULONG_MAX;

	return regs->b0;
}

/*
 * handle tracing of system call exit
 */
asmlinkage void syscall_trace_exit(struct pt_regs *regs)
{
	tracehook_report_syscall_exit(regs, 0);
}