Commit | Line | Data |
---|---|---|
c08098f2 VG |
1 | /* |
2 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/ptrace.h> | |
547f1125 VG |
10 | #include <linux/tracehook.h> |
11 | #include <linux/regset.h> | |
12 | #include <linux/unistd.h> | |
13 | #include <linux/elf.h> | |
14 | ||
15 | static struct callee_regs *task_callee_regs(struct task_struct *tsk) | |
16 | { | |
17 | struct callee_regs *tmp = (struct callee_regs *)tsk->thread.callee_reg; | |
18 | return tmp; | |
19 | } | |
20 | ||
21 | static int genregs_get(struct task_struct *target, | |
22 | const struct user_regset *regset, | |
23 | unsigned int pos, unsigned int count, | |
24 | void *kbuf, void __user *ubuf) | |
25 | { | |
26 | const struct pt_regs *ptregs = task_pt_regs(target); | |
27 | const struct callee_regs *cregs = task_callee_regs(target); | |
28 | int ret = 0; | |
29 | unsigned int stop_pc_val; | |
30 | ||
31 | #define REG_O_CHUNK(START, END, PTR) \ | |
32 | if (!ret) \ | |
33 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ | |
34 | offsetof(struct user_regs_struct, START), \ | |
35 | offsetof(struct user_regs_struct, END)); | |
36 | ||
37 | #define REG_O_ONE(LOC, PTR) \ | |
38 | if (!ret) \ | |
39 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ | |
40 | offsetof(struct user_regs_struct, LOC), \ | |
41 | offsetof(struct user_regs_struct, LOC) + 4); | |
42 | ||
43 | REG_O_CHUNK(scratch, callee, ptregs); | |
44 | REG_O_CHUNK(callee, efa, cregs); | |
45 | REG_O_CHUNK(efa, stop_pc, &target->thread.fault_address); | |
46 | ||
47 | if (!ret) { | |
48 | if (in_brkpt_trap(ptregs)) { | |
49 | stop_pc_val = target->thread.fault_address; | |
50 | pr_debug("\t\tstop_pc (brk-pt)\n"); | |
51 | } else { | |
52 | stop_pc_val = ptregs->ret; | |
53 | pr_debug("\t\tstop_pc (others)\n"); | |
54 | } | |
55 | ||
56 | REG_O_ONE(stop_pc, &stop_pc_val); | |
57 | } | |
58 | ||
59 | return ret; | |
60 | } | |
61 | ||
62 | static int genregs_set(struct task_struct *target, | |
63 | const struct user_regset *regset, | |
64 | unsigned int pos, unsigned int count, | |
65 | const void *kbuf, const void __user *ubuf) | |
66 | { | |
67 | const struct pt_regs *ptregs = task_pt_regs(target); | |
68 | const struct callee_regs *cregs = task_callee_regs(target); | |
69 | int ret = 0; | |
70 | ||
71 | #define REG_IN_CHUNK(FIRST, NEXT, PTR) \ | |
72 | if (!ret) \ | |
73 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ | |
74 | (void *)(PTR), \ | |
75 | offsetof(struct user_regs_struct, FIRST), \ | |
76 | offsetof(struct user_regs_struct, NEXT)); | |
77 | ||
78 | #define REG_IN_ONE(LOC, PTR) \ | |
79 | if (!ret) \ | |
80 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ | |
81 | (void *)(PTR), \ | |
82 | offsetof(struct user_regs_struct, LOC), \ | |
83 | offsetof(struct user_regs_struct, LOC) + 4); | |
84 | ||
85 | #define REG_IGNORE_ONE(LOC) \ | |
86 | if (!ret) \ | |
87 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ | |
88 | offsetof(struct user_regs_struct, LOC), \ | |
89 | offsetof(struct user_regs_struct, LOC) + 4); | |
90 | ||
91 | /* TBD: disallow updates to STATUS32, orig_r8 etc*/ | |
92 | REG_IN_CHUNK(scratch, callee, ptregs); /* pt_regs[bta..orig_r8] */ | |
93 | REG_IN_CHUNK(callee, efa, cregs); /* callee_regs[r25..r13] */ | |
94 | REG_IGNORE_ONE(efa); /* efa update invalid */ | |
4b3ea63f | 95 | REG_IGNORE_ONE(stop_pc); /* PC updated via @ret */ |
547f1125 VG |
96 | |
97 | return ret; | |
98 | } | |
99 | ||
100 | enum arc_getset { | |
101 | REGSET_GENERAL, | |
102 | }; | |
103 | ||
104 | static const struct user_regset arc_regsets[] = { | |
105 | [REGSET_GENERAL] = { | |
106 | .core_note_type = NT_PRSTATUS, | |
107 | .n = ELF_NGREG, | |
108 | .size = sizeof(unsigned long), | |
109 | .align = sizeof(unsigned long), | |
110 | .get = genregs_get, | |
111 | .set = genregs_set, | |
112 | } | |
113 | }; | |
114 | ||
115 | static const struct user_regset_view user_arc_view = { | |
116 | .name = UTS_MACHINE, | |
117 | .e_machine = EM_ARCOMPACT, | |
118 | .regsets = arc_regsets, | |
119 | .n = ARRAY_SIZE(arc_regsets) | |
120 | }; | |
121 | ||
122 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
123 | { | |
124 | return &user_arc_view; | |
125 | } | |
c08098f2 VG |
126 | |
127 | void ptrace_disable(struct task_struct *child) | |
128 | { | |
129 | } | |
130 | ||
131 | long arch_ptrace(struct task_struct *child, long request, | |
132 | unsigned long addr, unsigned long data) | |
133 | { | |
134 | int ret = -EIO; | |
547f1125 VG |
135 | |
136 | pr_debug("REQ=%ld: ADDR =0x%lx, DATA=0x%lx)\n", request, addr, data); | |
137 | ||
138 | switch (request) { | |
139 | default: | |
140 | ret = ptrace_request(child, request, addr, data); | |
141 | break; | |
142 | } | |
143 | ||
c08098f2 VG |
144 | return ret; |
145 | } | |
146 | ||
547f1125 VG |
147 | asmlinkage int syscall_trace_entry(struct pt_regs *regs) |
148 | { | |
149 | if (tracehook_report_syscall_entry(regs)) | |
150 | return ULONG_MAX; | |
c08098f2 | 151 | |
547f1125 VG |
152 | return regs->r8; |
153 | } | |
154 | ||
155 | asmlinkage void syscall_trace_exit(struct pt_regs *regs) | |
c08098f2 | 156 | { |
547f1125 | 157 | tracehook_report_syscall_exit(regs, 0); |
c08098f2 | 158 | } |