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 atomic_set(&pp
->cpu_count
, -1);
161 while (atomic_read(&pp
->cpu_count
) != -1)
169 int __kprobes
aarch64_insn_patch_text_sync(void *addrs
[], u32 insns
[], int cnt
)
171 struct aarch64_insn_patch patch
= {
175 .cpu_count
= ATOMIC_INIT(0),
181 return stop_machine(aarch64_insn_patch_text_cb
, &patch
,
185 int __kprobes
aarch64_insn_patch_text(void *addrs
[], u32 insns
[], int cnt
)
190 /* Unsafe to patch multiple instructions without synchronizaiton */
192 ret
= aarch64_insn_read(addrs
[0], &insn
);
196 if (aarch64_insn_hotpatch_safe(insn
, insns
[0])) {
198 * ARMv8 architecture doesn't guarantee all CPUs see
199 * the new instruction after returning from function
200 * aarch64_insn_patch_text_nosync(). So send IPIs to
201 * all other CPUs to achieve instruction
204 ret
= aarch64_insn_patch_text_nosync(addrs
[0], insns
[0]);
205 kick_all_cpus_sync();
210 return aarch64_insn_patch_text_sync(addrs
, insns
, cnt
);
213 u32 __kprobes
aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type
,
216 u32 immlo
, immhi
, lomask
, himask
, mask
;
220 case AARCH64_INSN_IMM_ADR
:
223 immlo
= imm
& lomask
;
225 immhi
= imm
& himask
;
226 imm
= (immlo
<< 24) | (immhi
);
227 mask
= (lomask
<< 24) | (himask
);
230 case AARCH64_INSN_IMM_26
:
234 case AARCH64_INSN_IMM_19
:
238 case AARCH64_INSN_IMM_16
:
242 case AARCH64_INSN_IMM_14
:
246 case AARCH64_INSN_IMM_12
:
250 case AARCH64_INSN_IMM_9
:
255 pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n",
260 /* Update the immediate field. */
261 insn
&= ~(mask
<< shift
);
262 insn
|= (imm
& mask
) << shift
;
267 u32 __kprobes
aarch64_insn_gen_branch_imm(unsigned long pc
, unsigned long addr
,
268 enum aarch64_insn_branch_type type
)
274 * PC: A 64-bit Program Counter holding the address of the current
275 * instruction. A64 instructions must be word-aligned.
277 BUG_ON((pc
& 0x3) || (addr
& 0x3));
280 * B/BL support [-128M, 128M) offset
281 * ARM64 virtual address arrangement guarantees all kernel and module
282 * texts are within +/-128M.
284 offset
= ((long)addr
- (long)pc
);
285 BUG_ON(offset
< -SZ_128M
|| offset
>= SZ_128M
);
287 if (type
== AARCH64_INSN_BRANCH_LINK
)
288 insn
= aarch64_insn_get_bl_value();
290 insn
= aarch64_insn_get_b_value();
292 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26
, insn
,
296 u32 __kprobes
aarch64_insn_gen_hint(enum aarch64_insn_hint_op op
)
298 return aarch64_insn_get_hint_value() | op
;
301 u32 __kprobes
aarch64_insn_gen_nop(void)
303 return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP
);