1 | /* |
2 | * Copyright (C) 2014 Altera Corporation |
3 | * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> |
4 | * |
5 | * This file is subject to the terms and conditions of the GNU General |
6 | * Public License. See the file COPYING in the main directory of this |
7 | * archive for more details. |
8 | */ |
9 | |
10 | #include <linux/elf.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/mm.h> |
14 | #include <linux/ptrace.h> |
15 | #include <linux/regset.h> |
16 | #include <linux/sched.h> |
17 | #include <linux/sched/task_stack.h> |
18 | #include <linux/uaccess.h> |
19 | #include <linux/user.h> |
20 | |
21 | static int genregs_get(struct task_struct *target, |
22 | const struct user_regset *regset, |
23 | struct membuf to) |
24 | { |
25 | const struct pt_regs *regs = task_pt_regs(target); |
26 | const struct switch_stack *sw = (struct switch_stack *)regs - 1; |
27 | |
28 | membuf_zero(s: &to, size: 4); // R0 |
29 | membuf_write(s: &to, v: ®s->r1, size: 7 * 4); // R1..R7 |
30 | membuf_write(s: &to, v: ®s->r8, size: 8 * 4); // R8..R15 |
31 | membuf_write(s: &to, v: sw, size: 8 * 4); // R16..R23 |
32 | membuf_zero(s: &to, size: 2 * 4); /* et and bt */ |
33 | membuf_store(&to, regs->gp); |
34 | membuf_store(&to, regs->sp); |
35 | membuf_store(&to, regs->fp); |
36 | membuf_store(&to, regs->ea); |
37 | membuf_zero(s: &to, size: 4); // PTR_BA |
38 | membuf_store(&to, regs->ra); |
39 | membuf_store(&to, regs->ea); /* use ea for PC */ |
40 | return membuf_zero(&to, (NUM_PTRACE_REG - PTR_PC) * 4); |
41 | } |
42 | |
43 | /* |
44 | * Set the thread state from a regset passed in via ptrace |
45 | */ |
46 | static int genregs_set(struct task_struct *target, |
47 | const struct user_regset *regset, |
48 | unsigned int pos, unsigned int count, |
49 | const void *kbuf, const void __user *ubuf) |
50 | { |
51 | struct pt_regs *regs = task_pt_regs(target); |
52 | const struct switch_stack *sw = (struct switch_stack *)regs - 1; |
53 | int ret = 0; |
54 | |
55 | #define REG_IGNORE_RANGE(START, END) \ |
56 | if (!ret) \ |
57 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ |
58 | START * 4, (END * 4) + 4); |
59 | |
60 | #define REG_IN_ONE(PTR, LOC) \ |
61 | if (!ret) \ |
62 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ |
63 | (void *)(PTR), LOC * 4, (LOC * 4) + 4); |
64 | |
65 | #define REG_IN_RANGE(PTR, START, END) \ |
66 | if (!ret) \ |
67 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ |
68 | (void *)(PTR), START * 4, (END * 4) + 4); |
69 | |
70 | REG_IGNORE_RANGE(PTR_R0, PTR_R0); |
71 | REG_IN_RANGE(®s->r1, PTR_R1, PTR_R7); |
72 | REG_IN_RANGE(®s->r8, PTR_R8, PTR_R15); |
73 | REG_IN_RANGE(sw, PTR_R16, PTR_R23); |
74 | REG_IGNORE_RANGE(PTR_R24, PTR_R25); /* et and bt */ |
75 | REG_IN_ONE(®s->gp, PTR_GP); |
76 | REG_IN_ONE(®s->sp, PTR_SP); |
77 | REG_IN_ONE(®s->fp, PTR_FP); |
78 | REG_IN_ONE(®s->ea, PTR_EA); |
79 | REG_IGNORE_RANGE(PTR_BA, PTR_BA); |
80 | REG_IN_ONE(®s->ra, PTR_RA); |
81 | REG_IN_ONE(®s->ea, PTR_PC); /* use ea for PC */ |
82 | if (!ret) |
83 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
84 | start_pos: PTR_STATUS * 4, end_pos: -1); |
85 | |
86 | return ret; |
87 | } |
88 | |
89 | /* |
90 | * Define the register sets available on Nios2 under Linux |
91 | */ |
92 | enum nios2_regset { |
93 | REGSET_GENERAL, |
94 | }; |
95 | |
96 | static const struct user_regset nios2_regsets[] = { |
97 | [REGSET_GENERAL] = { |
98 | .core_note_type = NT_PRSTATUS, |
99 | .n = NUM_PTRACE_REG, |
100 | .size = sizeof(unsigned long), |
101 | .align = sizeof(unsigned long), |
102 | .regset_get = genregs_get, |
103 | .set = genregs_set, |
104 | } |
105 | }; |
106 | |
107 | static const struct user_regset_view nios2_user_view = { |
108 | .name = "nios2" , |
109 | .e_machine = ELF_ARCH, |
110 | .ei_osabi = ELF_OSABI, |
111 | .regsets = nios2_regsets, |
112 | .n = ARRAY_SIZE(nios2_regsets) |
113 | }; |
114 | |
115 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
116 | { |
117 | return &nios2_user_view; |
118 | } |
119 | |
120 | void ptrace_disable(struct task_struct *child) |
121 | { |
122 | |
123 | } |
124 | |
125 | long arch_ptrace(struct task_struct *child, long request, unsigned long addr, |
126 | unsigned long data) |
127 | { |
128 | return ptrace_request(child, request, addr, data); |
129 | } |
130 | |
131 | asmlinkage int do_syscall_trace_enter(void) |
132 | { |
133 | int ret = 0; |
134 | |
135 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
136 | ret = ptrace_report_syscall_entry(task_pt_regs(current)); |
137 | |
138 | return ret; |
139 | } |
140 | |
141 | asmlinkage void do_syscall_trace_exit(void) |
142 | { |
143 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
144 | ptrace_report_syscall_exit(task_pt_regs(current), step: 0); |
145 | } |
146 | |