aboutsummaryrefslogtreecommitdiffstats
path: root/arch/openrisc/kernel
diff options
context:
space:
mode:
authorJonas Bonn <jonas@southpole.se>2011-06-04 14:54:22 -0400
committerJonas Bonn <jonas@southpole.se>2011-07-22 12:46:31 -0400
commite5ad95ce9b8d7efc443d39a7bbc4e55b7a4593f1 (patch)
tree5aaa5deea8835785d8d62e42079d38ac0ea2b166 /arch/openrisc/kernel
parentf8c4a270d9330a2bc179aeef0a22ea1ed288fb50 (diff)
OpenRISC: PTrace
This patch implements minimal PTrace support. The pt_regs structure is not exported to userspace for OpenRISC; rather, the GETREGSET mechanism is intended to be used and the registers, as such, exported in the core dump format which is ABI stable. This is in line with what is intended for new architectures as of 2.6.34 and has the advantage of permitting the layout of the registers on the kernel stack (as per pt_regs) to be freely modified. Signed-off-by: Jonas Bonn <jonas@southpole.se> Reviewed-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/openrisc/kernel')
-rw-r--r--arch/openrisc/kernel/ptrace.c211
1 files changed, 211 insertions, 0 deletions
diff --git a/arch/openrisc/kernel/ptrace.c b/arch/openrisc/kernel/ptrace.c
new file mode 100644
index 000000000000..656b94beab89
--- /dev/null
+++ b/arch/openrisc/kernel/ptrace.c
@@ -0,0 +1,211 @@
1/*
2 * OpenRISC ptrace.c
3 *
4 * Linux architectural port borrowing liberally from similar works of
5 * others. All original copyrights apply as per the original source
6 * declaration.
7 *
8 * Modifications for the OpenRISC architecture:
9 * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
10 * Copyright (C) 2005 Gyorgy Jeney <nog@bsemi.com>
11 * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version
16 * 2 of the License, or (at your option) any later version.
17 */
18
19#include <stddef.h>
20#include <linux/kernel.h>
21#include <linux/sched.h>
22#include <linux/string.h>
23
24#include <linux/mm.h>
25#include <linux/errno.h>
26#include <linux/ptrace.h>
27#include <linux/audit.h>
28#include <linux/regset.h>
29#include <linux/tracehook.h>
30#include <linux/elf.h>
31
32#include <asm/thread_info.h>
33#include <asm/segment.h>
34#include <asm/page.h>
35#include <asm/pgtable.h>
36#include <asm/system.h>
37
38/*
39 * Copy the thread state to a regset that can be interpreted by userspace.
40 *
41 * It doesn't matter what our internal pt_regs structure looks like. The
42 * important thing is that we export a consistent view of the thread state
43 * to userspace. As such, we need to make sure that the regset remains
44 * ABI compatible as defined by the struct user_regs_struct:
45 *
46 * (Each item is a 32-bit word)
47 * r0 = 0 (exported for clarity)
48 * 31 GPRS r1-r31
49 * PC (Program counter)
50 * SR (Supervision register)
51 */
52static int genregs_get(struct task_struct *target,
53 const struct user_regset *regset,
54 unsigned int pos, unsigned int count,
55 void *kbuf, void __user * ubuf)
56{
57 const struct pt_regs *regs = task_pt_regs(target);
58 int ret;
59
60 /* r0 */
61 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 0, 4);
62
63 if (!ret)
64 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
65 regs->gpr+1, 4, 4*32);
66 if (!ret)
67 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
68 &regs->pc, 4*32, 4*33);
69 if (!ret)
70 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
71 &regs->sr, 4*33, 4*34);
72 if (!ret)
73 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
74 4*34, -1);
75
76 return ret;
77}
78
79/*
80 * Set the thread state from a regset passed in via ptrace
81 */
82static int genregs_set(struct task_struct *target,
83 const struct user_regset *regset,
84 unsigned int pos, unsigned int count,
85 const void *kbuf, const void __user * ubuf)
86{
87 struct pt_regs *regs = task_pt_regs(target);
88 int ret;
89
90 /* ignore r0 */
91 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, 4);
92 /* r1 - r31 */
93 if (!ret)
94 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
95 regs->gpr+1, 4, 4*32);
96 /* PC */
97 if (!ret)
98 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
99 &regs->pc, 4*32, 4*33);
100 /*
101 * Skip SR and padding... userspace isn't allowed to changes bits in
102 * the Supervision register
103 */
104 if (!ret)
105 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
106 4*33, -1);
107
108 return ret;
109}
110
111/*
112 * Define the register sets available on OpenRISC under Linux
113 */
114enum or1k_regset {
115 REGSET_GENERAL,
116};
117
118static const struct user_regset or1k_regsets[] = {
119 [REGSET_GENERAL] = {
120 .core_note_type = NT_PRSTATUS,
121 .n = ELF_NGREG,
122 .size = sizeof(long),
123 .align = sizeof(long),
124 .get = genregs_get,
125 .set = genregs_set,
126 },
127};
128
129static const struct user_regset_view user_or1k_native_view = {
130 .name = "or1k",
131 .e_machine = EM_OPENRISC,
132 .regsets = or1k_regsets,
133 .n = ARRAY_SIZE(or1k_regsets),
134};
135
136const struct user_regset_view *task_user_regset_view(struct task_struct *task)
137{
138 return &user_or1k_native_view;
139}
140
141/*
142 * does not yet catch signals sent when the child dies.
143 * in exit.c or in signal.c.
144 */
145
146
147/*
148 * Called by kernel/ptrace.c when detaching..
149 *
150 * Make sure the single step bit is not set.
151 */
152void ptrace_disable(struct task_struct *child)
153{
154 pr_debug("ptrace_disable(): TODO\n");
155
156 user_disable_single_step(child);
157 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
158}
159
160long arch_ptrace(struct task_struct *child, long request, unsigned long addr,
161 unsigned long data)
162{
163 int ret;
164
165 switch (request) {
166 default:
167 ret = ptrace_request(child, request, addr, data);
168 break;
169 }
170
171 return ret;
172}
173
174/*
175 * Notification of system call entry/exit
176 * - triggered by current->work.syscall_trace
177 */
178asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
179{
180 long ret = 0;
181
182 if (test_thread_flag(TIF_SYSCALL_TRACE) &&
183 tracehook_report_syscall_entry(regs))
184 /*
185 * Tracing decided this syscall should not happen.
186 * We'll return a bogus call number to get an ENOSYS
187 * error, but leave the original number in <something>.
188 */
189 ret = -1L;
190
191 /* Are these regs right??? */
192 if (unlikely(current->audit_context))
193 audit_syscall_entry(audit_arch(), regs->syscallno,
194 regs->gpr[3], regs->gpr[4],
195 regs->gpr[5], regs->gpr[6]);
196
197 return ret ? : regs->syscallno;
198}
199
200asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
201{
202 int step;
203
204 if (unlikely(current->audit_context))
205 audit_syscall_exit(AUDITSC_RESULT(regs->gpr[11]),
206 regs->gpr[11]);
207
208 step = test_thread_flag(TIF_SINGLESTEP);
209 if (step || test_thread_flag(TIF_SYSCALL_TRACE))
210 tracehook_report_syscall_exit(regs, step);
211}