1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Ptrace support for Hexagon |
4 | * |
5 | * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/sched.h> |
10 | #include <linux/sched/task_stack.h> |
11 | #include <linux/mm.h> |
12 | #include <linux/smp.h> |
13 | #include <linux/errno.h> |
14 | #include <linux/ptrace.h> |
15 | #include <linux/regset.h> |
16 | #include <linux/user.h> |
17 | #include <linux/elf.h> |
18 | |
19 | #include <asm/user.h> |
20 | |
21 | #if arch_has_single_step() |
22 | /* Both called from ptrace_resume */ |
23 | void user_enable_single_step(struct task_struct *child) |
24 | { |
25 | pt_set_singlestep(task_pt_regs(child)); |
26 | set_tsk_thread_flag(tsk: child, TIF_SINGLESTEP); |
27 | } |
28 | |
29 | void user_disable_single_step(struct task_struct *child) |
30 | { |
31 | pt_clr_singlestep(task_pt_regs(child)); |
32 | clear_tsk_thread_flag(tsk: child, TIF_SINGLESTEP); |
33 | } |
34 | #endif |
35 | |
36 | static int genregs_get(struct task_struct *target, |
37 | const struct user_regset *regset, |
38 | struct membuf to) |
39 | { |
40 | struct pt_regs *regs = task_pt_regs(target); |
41 | |
42 | /* The general idea here is that the copyout must happen in |
43 | * exactly the same order in which the userspace expects these |
44 | * regs. Now, the sequence in userspace does not match the |
45 | * sequence in the kernel, so everything past the 32 gprs |
46 | * happens one at a time. |
47 | */ |
48 | membuf_write(s: &to, v: ®s->r00, size: 32*sizeof(unsigned long)); |
49 | /* Must be exactly same sequence as struct user_regs_struct */ |
50 | membuf_store(&to, regs->sa0); |
51 | membuf_store(&to, regs->lc0); |
52 | membuf_store(&to, regs->sa1); |
53 | membuf_store(&to, regs->lc1); |
54 | membuf_store(&to, regs->m0); |
55 | membuf_store(&to, regs->m1); |
56 | membuf_store(&to, regs->usr); |
57 | membuf_store(&to, regs->preds); |
58 | membuf_store(&to, regs->gp); |
59 | membuf_store(&to, regs->ugp); |
60 | membuf_store(&to, pt_elr(regs)); // pc |
61 | membuf_store(&to, (unsigned long)pt_cause(regs)); // cause |
62 | membuf_store(&to, pt_badva(regs)); // badva |
63 | #if CONFIG_HEXAGON_ARCH_VERSION >=4 |
64 | membuf_store(&to, regs->cs0); |
65 | membuf_store(&to, regs->cs1); |
66 | return membuf_zero(&to, sizeof(unsigned long)); |
67 | #else |
68 | return membuf_zero(s: &to, size: 3 * sizeof(unsigned long)); |
69 | #endif |
70 | } |
71 | |
72 | static int genregs_set(struct task_struct *target, |
73 | const struct user_regset *regset, |
74 | unsigned int pos, unsigned int count, |
75 | const void *kbuf, const void __user *ubuf) |
76 | { |
77 | int ret, ignore_offset; |
78 | unsigned long bucket; |
79 | struct pt_regs *regs = task_pt_regs(target); |
80 | |
81 | if (!regs) |
82 | return -EIO; |
83 | |
84 | ret = user_regset_copyin(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
85 | data: ®s->r00, start_pos: 0, end_pos: 32*sizeof(unsigned long)); |
86 | |
87 | #define INEXT(KPT_REG, USR_REG) \ |
88 | if (!ret) \ |
89 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ |
90 | KPT_REG, offsetof(struct user_regs_struct, USR_REG), \ |
91 | offsetof(struct user_regs_struct, USR_REG) + \ |
92 | sizeof(unsigned long)); |
93 | |
94 | /* Must be exactly same sequence as struct user_regs_struct */ |
95 | INEXT(®s->sa0, sa0); |
96 | INEXT(®s->lc0, lc0); |
97 | INEXT(®s->sa1, sa1); |
98 | INEXT(®s->lc1, lc1); |
99 | INEXT(®s->m0, m0); |
100 | INEXT(®s->m1, m1); |
101 | INEXT(®s->usr, usr); |
102 | INEXT(®s->preds, p3_0); |
103 | INEXT(®s->gp, gp); |
104 | INEXT(®s->ugp, ugp); |
105 | INEXT(&pt_elr(regs), pc); |
106 | |
107 | /* CAUSE and BADVA aren't writeable. */ |
108 | INEXT(&bucket, cause); |
109 | INEXT(&bucket, badva); |
110 | |
111 | #if CONFIG_HEXAGON_ARCH_VERSION >=4 |
112 | INEXT(®s->cs0, cs0); |
113 | INEXT(®s->cs1, cs1); |
114 | ignore_offset = offsetof(struct user_regs_struct, pad1); |
115 | #else |
116 | ignore_offset = offsetof(struct user_regs_struct, cs0); |
117 | #endif |
118 | |
119 | /* Ignore the rest, if needed */ |
120 | if (!ret) |
121 | user_regset_copyin_ignore(pos: &pos, count: &count, kbuf: &kbuf, ubuf: &ubuf, |
122 | start_pos: ignore_offset, end_pos: -1); |
123 | else |
124 | return ret; |
125 | |
126 | /* |
127 | * This is special; SP is actually restored by the VM via the |
128 | * special event record which is set by the special trap. |
129 | */ |
130 | regs->hvmer.vmpsp = regs->r29; |
131 | return 0; |
132 | } |
133 | |
134 | enum hexagon_regset { |
135 | REGSET_GENERAL, |
136 | }; |
137 | |
138 | static const struct user_regset hexagon_regsets[] = { |
139 | [REGSET_GENERAL] = { |
140 | .core_note_type = NT_PRSTATUS, |
141 | .n = ELF_NGREG, |
142 | .size = sizeof(unsigned long), |
143 | .align = sizeof(unsigned long), |
144 | .regset_get = genregs_get, |
145 | .set = genregs_set, |
146 | }, |
147 | }; |
148 | |
149 | static const struct user_regset_view hexagon_user_view = { |
150 | .name = "hexagon" , |
151 | .e_machine = ELF_ARCH, |
152 | .ei_osabi = ELF_OSABI, |
153 | .regsets = hexagon_regsets, |
154 | .e_flags = ELF_CORE_EFLAGS, |
155 | .n = ARRAY_SIZE(hexagon_regsets) |
156 | }; |
157 | |
158 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
159 | { |
160 | return &hexagon_user_view; |
161 | } |
162 | |
163 | void ptrace_disable(struct task_struct *child) |
164 | { |
165 | /* Boilerplate - resolves to null inline if no HW single-step */ |
166 | user_disable_single_step(child); |
167 | } |
168 | |
169 | long arch_ptrace(struct task_struct *child, long request, |
170 | unsigned long addr, unsigned long data) |
171 | { |
172 | return ptrace_request(child, request, addr, data); |
173 | } |
174 | |