Commit | Line | Data |
---|---|---|
6dea1ba1 G |
1 | /* |
2 | * linux/arch/unicore32/kernel/ptrace.c | |
3 | * | |
4 | * Code specific to PKUnity SoC and UniCore ISA | |
5 | * | |
6 | * Copyright (C) 2001-2010 GUAN Xue-tao | |
7 | * | |
8 | * By Ross Biro 1/23/92 | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/ptrace.h> | |
16 | #include <linux/signal.h> | |
17 | #include <linux/uaccess.h> | |
18 | ||
19 | /* | |
20 | * this routine will get a word off of the processes privileged stack. | |
21 | * the offset is how far from the base addr as stored in the THREAD. | |
22 | * this routine assumes that all the privileged stacks are in our | |
23 | * data space. | |
24 | */ | |
25 | static inline long get_user_reg(struct task_struct *task, int offset) | |
26 | { | |
27 | return task_pt_regs(task)->uregs[offset]; | |
28 | } | |
29 | ||
30 | /* | |
31 | * this routine will put a word on the processes privileged stack. | |
32 | * the offset is how far from the base addr as stored in the THREAD. | |
33 | * this routine assumes that all the privileged stacks are in our | |
34 | * data space. | |
35 | */ | |
36 | static inline int | |
37 | put_user_reg(struct task_struct *task, int offset, long data) | |
38 | { | |
39 | struct pt_regs newregs, *regs = task_pt_regs(task); | |
40 | int ret = -EINVAL; | |
41 | ||
42 | newregs = *regs; | |
43 | newregs.uregs[offset] = data; | |
44 | ||
45 | if (valid_user_regs(&newregs)) { | |
46 | regs->uregs[offset] = data; | |
47 | ret = 0; | |
48 | } | |
49 | ||
50 | return ret; | |
51 | } | |
52 | ||
53 | /* | |
54 | * Called by kernel/ptrace.c when detaching.. | |
55 | */ | |
56 | void ptrace_disable(struct task_struct *child) | |
57 | { | |
58 | } | |
59 | ||
60 | /* | |
61 | * We actually access the pt_regs stored on the kernel stack. | |
62 | */ | |
63 | static int ptrace_read_user(struct task_struct *tsk, unsigned long off, | |
64 | unsigned long __user *ret) | |
65 | { | |
66 | unsigned long tmp; | |
67 | ||
68 | tmp = 0; | |
69 | if (off < sizeof(struct pt_regs)) | |
70 | tmp = get_user_reg(tsk, off >> 2); | |
71 | ||
72 | return put_user(tmp, ret); | |
73 | } | |
74 | ||
75 | /* | |
76 | * We actually access the pt_regs stored on the kernel stack. | |
77 | */ | |
78 | static int ptrace_write_user(struct task_struct *tsk, unsigned long off, | |
79 | unsigned long val) | |
80 | { | |
81 | if (off >= sizeof(struct pt_regs)) | |
82 | return 0; | |
83 | ||
84 | return put_user_reg(tsk, off >> 2, val); | |
85 | } | |
86 | ||
87 | long arch_ptrace(struct task_struct *child, long request, | |
88 | unsigned long addr, unsigned long data) | |
89 | { | |
90 | int ret; | |
91 | unsigned long __user *datap = (unsigned long __user *) data; | |
92 | ||
93 | switch (request) { | |
94 | case PTRACE_PEEKUSR: | |
95 | ret = ptrace_read_user(child, addr, datap); | |
96 | break; | |
97 | ||
98 | case PTRACE_POKEUSR: | |
99 | ret = ptrace_write_user(child, addr, data); | |
100 | break; | |
101 | ||
102 | case PTRACE_GET_THREAD_AREA: | |
103 | ret = put_user(task_pt_regs(child)->UCreg_16, | |
104 | datap); | |
105 | break; | |
106 | ||
107 | default: | |
108 | ret = ptrace_request(child, request, addr, data); | |
109 | break; | |
110 | } | |
111 | ||
112 | return ret; | |
113 | } | |
114 | ||
115 | asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno) | |
116 | { | |
117 | unsigned long ip; | |
118 | ||
119 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | |
120 | return scno; | |
121 | if (!(current->ptrace & PT_PTRACED)) | |
122 | return scno; | |
123 | ||
124 | /* | |
125 | * Save IP. IP is used to denote syscall entry/exit: | |
126 | * IP = 0 -> entry, = 1 -> exit | |
127 | */ | |
128 | ip = regs->UCreg_ip; | |
129 | regs->UCreg_ip = why; | |
130 | ||
131 | current_thread_info()->syscall = scno; | |
132 | ||
133 | /* the 0x80 provides a way for the tracing parent to distinguish | |
134 | between a syscall stop and SIGTRAP delivery */ | |
135 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | |
136 | ? 0x80 : 0)); | |
137 | /* | |
138 | * this isn't the same as continuing with a signal, but it will do | |
139 | * for normal use. strace only continues with a signal if the | |
140 | * stopping signal is not SIGTRAP. -brl | |
141 | */ | |
142 | if (current->exit_code) { | |
143 | send_sig(current->exit_code, current, 1); | |
144 | current->exit_code = 0; | |
145 | } | |
146 | regs->UCreg_ip = ip; | |
147 | ||
148 | return current_thread_info()->syscall; | |
149 | } |