]>
Commit | Line | Data |
---|---|---|
08dbd0f8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
39e89c9f RK |
2 | /* |
3 | * Ptrace support for Hexagon | |
4 | * | |
7c6a5df4 | 5 | * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. |
39e89c9f RK |
6 | */ |
7 | ||
39e89c9f RK |
8 | #include <linux/kernel.h> |
9 | #include <linux/sched.h> | |
68db0cf1 | 10 | #include <linux/sched/task_stack.h> |
39e89c9f RK |
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> | |
6bbbc30c | 17 | #include <linux/elf.h> |
39e89c9f | 18 | |
39e89c9f RK |
19 | #include <asm/user.h> |
20 | ||
7777746c RK |
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(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(child, TIF_SINGLESTEP); | |
33 | } | |
34 | #endif | |
35 | ||
39e89c9f RK |
36 | static int genregs_get(struct task_struct *target, |
37 | const struct user_regset *regset, | |
8320514c | 38 | srtuct membuf to) |
39e89c9f | 39 | { |
39e89c9f RK |
40 | struct pt_regs *regs = task_pt_regs(target); |
41 | ||
39e89c9f RK |
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 | */ | |
8320514c | 48 | membuf_write(&to, ®s->r00, 32*sizeof(unsigned long)); |
39e89c9f | 49 | /* Must be exactly same sequence as struct user_regs_struct */ |
8320514c AV |
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->p3_0); | |
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 | |
60c4ba99 | 63 | #if CONFIG_HEXAGON_ARCH_VERSION >=4 |
8320514c AV |
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(&to, 3 * sizeof(unsigned long)); | |
60c4ba99 | 69 | #endif |
39e89c9f RK |
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; | |
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, &count, &kbuf, &ubuf, | |
85 | ®s->r00, 0, 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 | ||
60c4ba99 RK |
111 | #if CONFIG_HEXAGON_ARCH_VERSION >=4 |
112 | INEXT(®s->cs0, cs0); | |
113 | INEXT(®s->cs1, cs1); | |
114 | #endif | |
115 | ||
39e89c9f RK |
116 | /* Ignore the rest, if needed */ |
117 | if (!ret) | |
118 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
119 | offsetof(struct user_regs_struct, pad1), -1); | |
120 | ||
121 | if (ret) | |
122 | return ret; | |
123 | ||
124 | /* | |
125 | * This is special; SP is actually restored by the VM via the | |
126 | * special event record which is set by the special trap. | |
127 | */ | |
128 | regs->hvmer.vmpsp = regs->r29; | |
129 | return 0; | |
130 | } | |
131 | ||
132 | enum hexagon_regset { | |
133 | REGSET_GENERAL, | |
134 | }; | |
135 | ||
136 | static const struct user_regset hexagon_regsets[] = { | |
137 | [REGSET_GENERAL] = { | |
138 | .core_note_type = NT_PRSTATUS, | |
139 | .n = ELF_NGREG, | |
140 | .size = sizeof(unsigned long), | |
141 | .align = sizeof(unsigned long), | |
8320514c | 142 | .regset_get = genregs_get, |
39e89c9f RK |
143 | .set = genregs_set, |
144 | }, | |
145 | }; | |
146 | ||
147 | static const struct user_regset_view hexagon_user_view = { | |
f231e433 | 148 | .name = "hexagon", |
39e89c9f RK |
149 | .e_machine = ELF_ARCH, |
150 | .ei_osabi = ELF_OSABI, | |
151 | .regsets = hexagon_regsets, | |
446b6cb8 | 152 | .e_flags = ELF_CORE_EFLAGS, |
39e89c9f RK |
153 | .n = ARRAY_SIZE(hexagon_regsets) |
154 | }; | |
155 | ||
156 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
157 | { | |
158 | return &hexagon_user_view; | |
159 | } | |
160 | ||
161 | void ptrace_disable(struct task_struct *child) | |
162 | { | |
163 | /* Boilerplate - resolves to null inline if no HW single-step */ | |
164 | user_disable_single_step(child); | |
165 | } | |
166 | ||
167 | long arch_ptrace(struct task_struct *child, long request, | |
168 | unsigned long addr, unsigned long data) | |
169 | { | |
170 | return ptrace_request(child, request, addr, data); | |
171 | } |