Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/parisc/kernel/kprobes.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* arch/parisc/kernel/kprobes.c
4
*
5
* PA-RISC kprobes implementation
6
*
7
* Copyright (c) 2019 Sven Schnelle <[email protected]>
8
* Copyright (c) 2022 Helge Deller <[email protected]>
9
*/
10
11
#include <linux/types.h>
12
#include <linux/kprobes.h>
13
#include <linux/slab.h>
14
#include <asm/cacheflush.h>
15
#include <asm/text-patching.h>
16
17
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
18
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
19
20
int __kprobes arch_prepare_kprobe(struct kprobe *p)
21
{
22
if ((unsigned long)p->addr & 3UL)
23
return -EINVAL;
24
25
p->ainsn.insn = get_insn_slot();
26
if (!p->ainsn.insn)
27
return -ENOMEM;
28
29
/*
30
* Set up new instructions. Second break instruction will
31
* trigger call of parisc_kprobe_ss_handler().
32
*/
33
p->opcode = *p->addr;
34
p->ainsn.insn[0] = p->opcode;
35
p->ainsn.insn[1] = PARISC_KPROBES_BREAK_INSN2;
36
37
flush_insn_slot(p);
38
return 0;
39
}
40
41
void __kprobes arch_remove_kprobe(struct kprobe *p)
42
{
43
if (!p->ainsn.insn)
44
return;
45
46
free_insn_slot(p->ainsn.insn, 0);
47
p->ainsn.insn = NULL;
48
}
49
50
void __kprobes arch_arm_kprobe(struct kprobe *p)
51
{
52
patch_text(p->addr, PARISC_KPROBES_BREAK_INSN);
53
}
54
55
void __kprobes arch_disarm_kprobe(struct kprobe *p)
56
{
57
patch_text(p->addr, p->opcode);
58
}
59
60
static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
61
{
62
kcb->prev_kprobe.kp = kprobe_running();
63
kcb->prev_kprobe.status = kcb->kprobe_status;
64
}
65
66
static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
67
{
68
__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
69
kcb->kprobe_status = kcb->prev_kprobe.status;
70
}
71
72
static inline void __kprobes set_current_kprobe(struct kprobe *p)
73
{
74
__this_cpu_write(current_kprobe, p);
75
}
76
77
static void __kprobes setup_singlestep(struct kprobe *p,
78
struct kprobe_ctlblk *kcb, struct pt_regs *regs)
79
{
80
kcb->iaoq[0] = regs->iaoq[0];
81
kcb->iaoq[1] = regs->iaoq[1];
82
instruction_pointer_set(regs, (unsigned long)p->ainsn.insn);
83
}
84
85
int __kprobes parisc_kprobe_break_handler(struct pt_regs *regs)
86
{
87
struct kprobe *p;
88
struct kprobe_ctlblk *kcb;
89
90
preempt_disable();
91
92
kcb = get_kprobe_ctlblk();
93
p = get_kprobe((unsigned long *)regs->iaoq[0]);
94
95
if (!p) {
96
preempt_enable_no_resched();
97
return 0;
98
}
99
100
if (kprobe_running()) {
101
/*
102
* We have reentered the kprobe_handler, since another kprobe
103
* was hit while within the handler, we save the original
104
* kprobes and single step on the instruction of the new probe
105
* without calling any user handlers to avoid recursive
106
* kprobes.
107
*/
108
save_previous_kprobe(kcb);
109
set_current_kprobe(p);
110
kprobes_inc_nmissed_count(p);
111
setup_singlestep(p, kcb, regs);
112
kcb->kprobe_status = KPROBE_REENTER;
113
return 1;
114
}
115
116
set_current_kprobe(p);
117
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
118
119
/* If we have no pre-handler or it returned 0, we continue with
120
* normal processing. If we have a pre-handler and it returned
121
* non-zero - which means user handler setup registers to exit
122
* to another instruction, we must skip the single stepping.
123
*/
124
125
if (!p->pre_handler || !p->pre_handler(p, regs)) {
126
setup_singlestep(p, kcb, regs);
127
kcb->kprobe_status = KPROBE_HIT_SS;
128
} else {
129
reset_current_kprobe();
130
preempt_enable_no_resched();
131
}
132
return 1;
133
}
134
135
int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs)
136
{
137
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
138
struct kprobe *p = kprobe_running();
139
140
if (!p)
141
return 0;
142
143
if (regs->iaoq[0] != (unsigned long)p->ainsn.insn+4)
144
return 0;
145
146
/* restore back original saved kprobe variables and continue */
147
if (kcb->kprobe_status == KPROBE_REENTER) {
148
restore_previous_kprobe(kcb);
149
return 1;
150
}
151
152
/* for absolute branch instructions we can copy iaoq_b. for relative
153
* branch instructions we need to calculate the new address based on the
154
* difference between iaoq_f and iaoq_b. We cannot use iaoq_b without
155
* modifications because it's based on our ainsn.insn address.
156
*/
157
158
if (p->post_handler)
159
p->post_handler(p, regs, 0);
160
161
switch (regs->iir >> 26) {
162
case 0x38: /* BE */
163
case 0x39: /* BE,L */
164
case 0x3a: /* BV */
165
case 0x3b: /* BVE */
166
/* for absolute branches, regs->iaoq[1] has already the right
167
* address
168
*/
169
regs->iaoq[0] = kcb->iaoq[1];
170
break;
171
default:
172
regs->iaoq[0] = kcb->iaoq[1];
173
regs->iaoq[1] = regs->iaoq[0] + 4;
174
break;
175
}
176
kcb->kprobe_status = KPROBE_HIT_SSDONE;
177
reset_current_kprobe();
178
return 1;
179
}
180
181
void __kretprobe_trampoline(void)
182
{
183
asm volatile("nop");
184
asm volatile("nop");
185
}
186
187
static int __kprobes trampoline_probe_handler(struct kprobe *p,
188
struct pt_regs *regs);
189
190
static struct kprobe trampoline_p = {
191
.pre_handler = trampoline_probe_handler
192
};
193
194
static int __kprobes trampoline_probe_handler(struct kprobe *p,
195
struct pt_regs *regs)
196
{
197
__kretprobe_trampoline_handler(regs, NULL);
198
199
return 1;
200
}
201
202
void arch_kretprobe_fixup_return(struct pt_regs *regs,
203
kprobe_opcode_t *correct_ret_addr)
204
{
205
regs->gr[2] = (unsigned long)correct_ret_addr;
206
}
207
208
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
209
struct pt_regs *regs)
210
{
211
ri->ret_addr = (kprobe_opcode_t *)regs->gr[2];
212
ri->fp = NULL;
213
214
/* Replace the return addr with trampoline addr. */
215
regs->gr[2] = (unsigned long)trampoline_p.addr;
216
}
217
218
int __kprobes arch_trampoline_kprobe(struct kprobe *p)
219
{
220
return p->addr == trampoline_p.addr;
221
}
222
223
int __init arch_init_kprobes(void)
224
{
225
trampoline_p.addr = (kprobe_opcode_t *)
226
dereference_function_descriptor(__kretprobe_trampoline);
227
return register_kprobe(&trampoline_p);
228
}
229
230