2 * Copyright (C) 2013 Huawei Ltd.
3 * Author: Jiang Liu <liuj97@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include <linux/bitops.h>
18 #include <linux/compiler.h>
19 #include <linux/kernel.h>
20 #include <linux/smp.h>
21 #include <linux/stop_machine.h>
22 #include <linux/uaccess.h>
23 #include <asm/cacheflush.h>
26 static int aarch64_insn_encoding_class
[] = {
27 AARCH64_INSN_CLS_UNKNOWN
,
28 AARCH64_INSN_CLS_UNKNOWN
,
29 AARCH64_INSN_CLS_UNKNOWN
,
30 AARCH64_INSN_CLS_UNKNOWN
,
31 AARCH64_INSN_CLS_LDST
,
32 AARCH64_INSN_CLS_DP_REG
,
33 AARCH64_INSN_CLS_LDST
,
34 AARCH64_INSN_CLS_DP_FPSIMD
,
35 AARCH64_INSN_CLS_DP_IMM
,
36 AARCH64_INSN_CLS_DP_IMM
,
37 AARCH64_INSN_CLS_BR_SYS
,
38 AARCH64_INSN_CLS_BR_SYS
,
39 AARCH64_INSN_CLS_LDST
,
40 AARCH64_INSN_CLS_DP_REG
,
41 AARCH64_INSN_CLS_LDST
,
42 AARCH64_INSN_CLS_DP_FPSIMD
,
45 enum aarch64_insn_encoding_class __kprobes
aarch64_get_insn_class(u32 insn
)
47 return aarch64_insn_encoding_class
[(insn
>> 25) & 0xf];
50 /* NOP is an alias of HINT */
51 bool __kprobes
aarch64_insn_is_nop(u32 insn
)
53 if (!aarch64_insn_is_hint(insn
))
56 switch (insn
& 0xFE0) {
57 case AARCH64_INSN_HINT_YIELD
:
58 case AARCH64_INSN_HINT_WFE
:
59 case AARCH64_INSN_HINT_WFI
:
60 case AARCH64_INSN_HINT_SEV
:
61 case AARCH64_INSN_HINT_SEVL
:
69 * In ARMv8-A, A64 instructions have a fixed length of 32 bits and are always
72 int __kprobes
aarch64_insn_read(void *addr
, u32
*insnp
)
77 ret
= probe_kernel_read(&val
, addr
, AARCH64_INSN_SIZE
);
79 *insnp
= le32_to_cpu(val
);
84 int __kprobes
aarch64_insn_write(void *addr
, u32 insn
)
86 insn
= cpu_to_le32(insn
);
87 return probe_kernel_write(addr
, &insn
, AARCH64_INSN_SIZE
);
90 static bool __kprobes
__aarch64_insn_hotpatch_safe(u32 insn
)
92 if (aarch64_get_insn_class(insn
) != AARCH64_INSN_CLS_BR_SYS
)
95 return aarch64_insn_is_b(insn
) ||
96 aarch64_insn_is_bl(insn
) ||
97 aarch64_insn_is_svc(insn
) ||
98 aarch64_insn_is_hvc(insn
) ||
99 aarch64_insn_is_smc(insn
) ||
100 aarch64_insn_is_brk(insn
) ||
101 aarch64_insn_is_nop(insn
);
105 * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a
106 * Section B2.6.5 "Concurrent modification and execution of instructions":
107 * Concurrent modification and execution of instructions can lead to the
108 * resulting instruction performing any behavior that can be achieved by
109 * executing any sequence of instructions that can be executed from the
110 * same Exception level, except where the instruction before modification
111 * and the instruction after modification is a B, BL, NOP, BKPT, SVC, HVC,
112 * or SMC instruction.
114 bool __kprobes
aarch64_insn_hotpatch_safe(u32 old_insn
, u32 new_insn
)
116 return __aarch64_insn_hotpatch_safe(old_insn
) &&
117 __aarch64_insn_hotpatch_safe(new_insn
);
120 int __kprobes
aarch64_insn_patch_text_nosync(void *addr
, u32 insn
)
125 /* A64 instructions must be word aligned */
126 if ((uintptr_t)tp
& 0x3)
129 ret
= aarch64_insn_write(tp
, insn
);
131 flush_icache_range((uintptr_t)tp
,
132 (uintptr_t)tp
+ AARCH64_INSN_SIZE
);
137 struct aarch64_insn_patch
{
144 static int __kprobes
aarch64_insn_patch_text_cb(void *arg
)
147 struct aarch64_insn_patch
*pp
= arg
;
149 /* The first CPU becomes master */
150 if (atomic_inc_return(&pp
->cpu_count
) == 1) {
151 for (i
= 0; ret
== 0 && i
< pp
->insn_cnt
; i
++)
152 ret
= aarch64_insn_patch_text_nosync(pp
->text_addrs
[i
],
155 * aarch64_insn_patch_text_nosync() calls flush_icache_range(),
156 * which ends with "dsb; isb" pair guaranteeing global
159 /* Notify other processors with an additional increment. */
160 atomic_inc(&pp
->cpu_count
);
162 while (atomic_read(&pp
->cpu_count
) <= num_online_cpus())
170 int __kprobes
aarch64_insn_patch_text_sync(void *addrs
[], u32 insns
[], int cnt
)
172 struct aarch64_insn_patch patch
= {
176 .cpu_count
= ATOMIC_INIT(0),
182 return stop_machine(aarch64_insn_patch_text_cb
, &patch
,
186 int __kprobes
aarch64_insn_patch_text(void *addrs
[], u32 insns
[], int cnt
)
191 /* Unsafe to patch multiple instructions without synchronizaiton */
193 ret
= aarch64_insn_read(addrs
[0], &insn
);
197 if (aarch64_insn_hotpatch_safe(insn
, insns
[0])) {
199 * ARMv8 architecture doesn't guarantee all CPUs see
200 * the new instruction after returning from function
201 * aarch64_insn_patch_text_nosync(). So send IPIs to
202 * all other CPUs to achieve instruction
205 ret
= aarch64_insn_patch_text_nosync(addrs
[0], insns
[0]);
206 kick_all_cpus_sync();
211 return aarch64_insn_patch_text_sync(addrs
, insns
, cnt
);
214 u32 __kprobes
aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type
,
217 u32 immlo
, immhi
, lomask
, himask
, mask
;
221 case AARCH64_INSN_IMM_ADR
:
224 immlo
= imm
& lomask
;
226 immhi
= imm
& himask
;
227 imm
= (immlo
<< 24) | (immhi
);
228 mask
= (lomask
<< 24) | (himask
);
231 case AARCH64_INSN_IMM_26
:
235 case AARCH64_INSN_IMM_19
:
239 case AARCH64_INSN_IMM_16
:
243 case AARCH64_INSN_IMM_14
:
247 case AARCH64_INSN_IMM_12
:
251 case AARCH64_INSN_IMM_9
:
256 pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n",
261 /* Update the immediate field. */
262 insn
&= ~(mask
<< shift
);
263 insn
|= (imm
& mask
) << shift
;
268 u32 __kprobes
aarch64_insn_gen_branch_imm(unsigned long pc
, unsigned long addr
,
269 enum aarch64_insn_branch_type type
)
275 * PC: A 64-bit Program Counter holding the address of the current
276 * instruction. A64 instructions must be word-aligned.
278 BUG_ON((pc
& 0x3) || (addr
& 0x3));
281 * B/BL support [-128M, 128M) offset
282 * ARM64 virtual address arrangement guarantees all kernel and module
283 * texts are within +/-128M.
285 offset
= ((long)addr
- (long)pc
);
286 BUG_ON(offset
< -SZ_128M
|| offset
>= SZ_128M
);
288 if (type
== AARCH64_INSN_BRANCH_LINK
)
289 insn
= aarch64_insn_get_bl_value();
291 insn
= aarch64_insn_get_b_value();
293 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26
, insn
,
297 u32 __kprobes
aarch64_insn_gen_hint(enum aarch64_insn_hint_op op
)
299 return aarch64_insn_get_hint_value() | op
;
302 u32 __kprobes
aarch64_insn_gen_nop(void)
304 return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP
);