blob: 2929adaad58716fdf55c6a41e49aefd48c5a179d [file] [log] [blame]
Jiang Liub11a64a2014-01-07 22:17:08 +08001/*
2 * Copyright (C) 2013 Huawei Ltd.
3 * Author: Jiang Liu <liuj97@gmail.com>
4 *
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08005 * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com>
Zi Shen Lim617d2fb2014-08-27 05:15:17 +01006 *
Jiang Liub11a64a2014-01-07 22:17:08 +08007 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
Jiang Liu5c5bf252014-01-07 22:17:11 +080019#include <linux/bitops.h>
Laura Abbott2f896d52015-01-22 01:36:05 +000020#include <linux/bug.h>
Jiang Liub11a64a2014-01-07 22:17:08 +080021#include <linux/compiler.h>
22#include <linux/kernel.h>
Laura Abbott2f896d52015-01-22 01:36:05 +000023#include <linux/mm.h>
Jiang Liuae164802014-01-07 22:17:09 +080024#include <linux/smp.h>
Laura Abbott2f896d52015-01-22 01:36:05 +000025#include <linux/spinlock.h>
Jiang Liuae164802014-01-07 22:17:09 +080026#include <linux/stop_machine.h>
Laura Abbott2f896d52015-01-22 01:36:05 +000027#include <linux/types.h>
Jiang Liuae164802014-01-07 22:17:09 +080028#include <linux/uaccess.h>
Mark Browna9ae04c2014-09-16 17:42:33 +010029
Jiang Liuae164802014-01-07 22:17:09 +080030#include <asm/cacheflush.h>
Mark Browna9ae04c2014-09-16 17:42:33 +010031#include <asm/debug-monitors.h>
Laura Abbott2f896d52015-01-22 01:36:05 +000032#include <asm/fixmap.h>
Jiang Liub11a64a2014-01-07 22:17:08 +080033#include <asm/insn.h>
Luis R. Rodriguez7d134b22017-02-27 14:26:56 -080034#include <asm/kprobes.h>
Jiang Liub11a64a2014-01-07 22:17:08 +080035
Zi Shen Lim617d2fb2014-08-27 05:15:17 +010036#define AARCH64_INSN_SF_BIT BIT(31)
Zi Shen Lim4a89d2c2014-08-27 05:15:23 +010037#define AARCH64_INSN_N_BIT BIT(22)
Zi Shen Lim617d2fb2014-08-27 05:15:17 +010038
Jiang Liub11a64a2014-01-07 22:17:08 +080039static int aarch64_insn_encoding_class[] = {
40 AARCH64_INSN_CLS_UNKNOWN,
41 AARCH64_INSN_CLS_UNKNOWN,
42 AARCH64_INSN_CLS_UNKNOWN,
43 AARCH64_INSN_CLS_UNKNOWN,
44 AARCH64_INSN_CLS_LDST,
45 AARCH64_INSN_CLS_DP_REG,
46 AARCH64_INSN_CLS_LDST,
47 AARCH64_INSN_CLS_DP_FPSIMD,
48 AARCH64_INSN_CLS_DP_IMM,
49 AARCH64_INSN_CLS_DP_IMM,
50 AARCH64_INSN_CLS_BR_SYS,
51 AARCH64_INSN_CLS_BR_SYS,
52 AARCH64_INSN_CLS_LDST,
53 AARCH64_INSN_CLS_DP_REG,
54 AARCH64_INSN_CLS_LDST,
55 AARCH64_INSN_CLS_DP_FPSIMD,
56};
57
58enum aarch64_insn_encoding_class __kprobes aarch64_get_insn_class(u32 insn)
59{
60 return aarch64_insn_encoding_class[(insn >> 25) & 0xf];
61}
62
63/* NOP is an alias of HINT */
64bool __kprobes aarch64_insn_is_nop(u32 insn)
65{
66 if (!aarch64_insn_is_hint(insn))
67 return false;
68
69 switch (insn & 0xFE0) {
70 case AARCH64_INSN_HINT_YIELD:
71 case AARCH64_INSN_HINT_WFE:
72 case AARCH64_INSN_HINT_WFI:
73 case AARCH64_INSN_HINT_SEV:
74 case AARCH64_INSN_HINT_SEVL:
75 return false;
76 default:
77 return true;
78 }
79}
80
Marc Zyngier10b48f72015-06-01 10:47:39 +010081bool aarch64_insn_is_branch_imm(u32 insn)
82{
83 return (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn) ||
84 aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn) ||
85 aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
86 aarch64_insn_is_bcond(insn));
87}
88
Yang Shiabffa6f2015-09-30 19:23:12 +010089static DEFINE_RAW_SPINLOCK(patch_lock);
Laura Abbott2f896d52015-01-22 01:36:05 +000090
91static void __kprobes *patch_map(void *addr, int fixmap)
92{
93 unsigned long uintaddr = (uintptr_t) addr;
94 bool module = !core_kernel_text(uintaddr);
95 struct page *page;
96
Laura Abbott0f5bf6d2017-02-06 16:31:58 -080097 if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
Laura Abbott2f896d52015-01-22 01:36:05 +000098 page = vmalloc_to_page(addr);
Mark Rutland40982fd2016-08-25 17:23:23 +010099 else if (!module)
Laura Abbott2077be62017-01-10 13:35:49 -0800100 page = phys_to_page(__pa_symbol(addr));
Marc Zyngierf6242ca2015-02-24 16:30:21 +0000101 else
102 return addr;
Laura Abbott2f896d52015-01-22 01:36:05 +0000103
104 BUG_ON(!page);
yalin wang51650dc2015-07-24 12:52:28 +0100105 return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
106 (uintaddr & ~PAGE_MASK));
Laura Abbott2f896d52015-01-22 01:36:05 +0000107}
108
109static void __kprobes patch_unmap(int fixmap)
110{
111 clear_fixmap(fixmap);
112}
Jiang Liuae164802014-01-07 22:17:09 +0800113/*
114 * In ARMv8-A, A64 instructions have a fixed length of 32 bits and are always
115 * little-endian.
116 */
117int __kprobes aarch64_insn_read(void *addr, u32 *insnp)
118{
119 int ret;
Luc Van Oostenryck65de1422017-06-28 16:58:09 +0200120 __le32 val;
Jiang Liuae164802014-01-07 22:17:09 +0800121
122 ret = probe_kernel_read(&val, addr, AARCH64_INSN_SIZE);
123 if (!ret)
124 *insnp = le32_to_cpu(val);
125
126 return ret;
127}
128
Luc Van Oostenryck57c13832017-06-28 16:58:11 +0200129static int __kprobes __aarch64_insn_write(void *addr, __le32 insn)
Laura Abbott2f896d52015-01-22 01:36:05 +0000130{
131 void *waddr = addr;
132 unsigned long flags = 0;
133 int ret;
134
Yang Shiabffa6f2015-09-30 19:23:12 +0100135 raw_spin_lock_irqsave(&patch_lock, flags);
Laura Abbott2f896d52015-01-22 01:36:05 +0000136 waddr = patch_map(addr, FIX_TEXT_POKE0);
137
138 ret = probe_kernel_write(waddr, &insn, AARCH64_INSN_SIZE);
139
140 patch_unmap(FIX_TEXT_POKE0);
Yang Shiabffa6f2015-09-30 19:23:12 +0100141 raw_spin_unlock_irqrestore(&patch_lock, flags);
Laura Abbott2f896d52015-01-22 01:36:05 +0000142
143 return ret;
144}
145
Jiang Liuae164802014-01-07 22:17:09 +0800146int __kprobes aarch64_insn_write(void *addr, u32 insn)
147{
Luc Van Oostenryck57c13832017-06-28 16:58:11 +0200148 return __aarch64_insn_write(addr, cpu_to_le32(insn));
Jiang Liuae164802014-01-07 22:17:09 +0800149}
150
Jiang Liub11a64a2014-01-07 22:17:08 +0800151static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn)
152{
153 if (aarch64_get_insn_class(insn) != AARCH64_INSN_CLS_BR_SYS)
154 return false;
155
156 return aarch64_insn_is_b(insn) ||
157 aarch64_insn_is_bl(insn) ||
158 aarch64_insn_is_svc(insn) ||
159 aarch64_insn_is_hvc(insn) ||
160 aarch64_insn_is_smc(insn) ||
161 aarch64_insn_is_brk(insn) ||
162 aarch64_insn_is_nop(insn);
163}
164
David A. Longd59bee82016-07-08 12:35:46 -0400165bool __kprobes aarch64_insn_uses_literal(u32 insn)
166{
167 /* ldr/ldrsw (literal), prfm */
168
169 return aarch64_insn_is_ldr_lit(insn) ||
170 aarch64_insn_is_ldrsw_lit(insn) ||
171 aarch64_insn_is_adr_adrp(insn) ||
172 aarch64_insn_is_prfm_lit(insn);
173}
174
175bool __kprobes aarch64_insn_is_branch(u32 insn)
176{
177 /* b, bl, cb*, tb*, b.cond, br, blr */
178
179 return aarch64_insn_is_b(insn) ||
180 aarch64_insn_is_bl(insn) ||
181 aarch64_insn_is_cbz(insn) ||
182 aarch64_insn_is_cbnz(insn) ||
183 aarch64_insn_is_tbz(insn) ||
184 aarch64_insn_is_tbnz(insn) ||
185 aarch64_insn_is_ret(insn) ||
186 aarch64_insn_is_br(insn) ||
187 aarch64_insn_is_blr(insn) ||
188 aarch64_insn_is_bcond(insn);
189}
190
Jiang Liub11a64a2014-01-07 22:17:08 +0800191/*
192 * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a
193 * Section B2.6.5 "Concurrent modification and execution of instructions":
194 * Concurrent modification and execution of instructions can lead to the
195 * resulting instruction performing any behavior that can be achieved by
196 * executing any sequence of instructions that can be executed from the
197 * same Exception level, except where the instruction before modification
198 * and the instruction after modification is a B, BL, NOP, BKPT, SVC, HVC,
199 * or SMC instruction.
200 */
201bool __kprobes aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn)
202{
203 return __aarch64_insn_hotpatch_safe(old_insn) &&
204 __aarch64_insn_hotpatch_safe(new_insn);
205}
Jiang Liuae164802014-01-07 22:17:09 +0800206
207int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn)
208{
209 u32 *tp = addr;
210 int ret;
211
212 /* A64 instructions must be word aligned */
213 if ((uintptr_t)tp & 0x3)
214 return -EINVAL;
215
216 ret = aarch64_insn_write(tp, insn);
217 if (ret == 0)
218 flush_icache_range((uintptr_t)tp,
219 (uintptr_t)tp + AARCH64_INSN_SIZE);
220
221 return ret;
222}
223
224struct aarch64_insn_patch {
225 void **text_addrs;
226 u32 *new_insns;
227 int insn_cnt;
228 atomic_t cpu_count;
229};
230
231static int __kprobes aarch64_insn_patch_text_cb(void *arg)
232{
233 int i, ret = 0;
234 struct aarch64_insn_patch *pp = arg;
235
236 /* The first CPU becomes master */
237 if (atomic_inc_return(&pp->cpu_count) == 1) {
238 for (i = 0; ret == 0 && i < pp->insn_cnt; i++)
239 ret = aarch64_insn_patch_text_nosync(pp->text_addrs[i],
240 pp->new_insns[i]);
241 /*
242 * aarch64_insn_patch_text_nosync() calls flush_icache_range(),
243 * which ends with "dsb; isb" pair guaranteeing global
244 * visibility.
245 */
William Cohen899d5932014-11-11 09:41:27 -0500246 /* Notify other processors with an additional increment. */
247 atomic_inc(&pp->cpu_count);
Jiang Liuae164802014-01-07 22:17:09 +0800248 } else {
William Cohen899d5932014-11-11 09:41:27 -0500249 while (atomic_read(&pp->cpu_count) <= num_online_cpus())
Jiang Liuae164802014-01-07 22:17:09 +0800250 cpu_relax();
251 isb();
252 }
253
254 return ret;
255}
256
Thomas Gleixnerc23a4652017-05-24 10:15:37 +0200257static
Jiang Liuae164802014-01-07 22:17:09 +0800258int __kprobes aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt)
259{
260 struct aarch64_insn_patch patch = {
261 .text_addrs = addrs,
262 .new_insns = insns,
263 .insn_cnt = cnt,
264 .cpu_count = ATOMIC_INIT(0),
265 };
266
267 if (cnt <= 0)
268 return -EINVAL;
269
Thomas Gleixnerc23a4652017-05-24 10:15:37 +0200270 return stop_machine_cpuslocked(aarch64_insn_patch_text_cb, &patch,
271 cpu_online_mask);
Jiang Liuae164802014-01-07 22:17:09 +0800272}
273
274int __kprobes aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt)
275{
276 int ret;
277 u32 insn;
278
279 /* Unsafe to patch multiple instructions without synchronizaiton */
280 if (cnt == 1) {
281 ret = aarch64_insn_read(addrs[0], &insn);
282 if (ret)
283 return ret;
284
285 if (aarch64_insn_hotpatch_safe(insn, insns[0])) {
286 /*
287 * ARMv8 architecture doesn't guarantee all CPUs see
288 * the new instruction after returning from function
289 * aarch64_insn_patch_text_nosync(). So send IPIs to
290 * all other CPUs to achieve instruction
291 * synchronization.
292 */
293 ret = aarch64_insn_patch_text_nosync(addrs[0], insns[0]);
294 kick_all_cpus_sync();
295 return ret;
296 }
297 }
298
299 return aarch64_insn_patch_text_sync(addrs, insns, cnt);
300}
Jiang Liuc84fced2014-01-07 22:17:10 +0800301
Marc Zyngier0978fb22015-03-27 13:09:21 +0000302static int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type,
303 u32 *maskp, int *shiftp)
Jiang Liuc84fced2014-01-07 22:17:10 +0800304{
Marc Zyngier0978fb22015-03-27 13:09:21 +0000305 u32 mask;
Jiang Liuc84fced2014-01-07 22:17:10 +0800306 int shift;
307
308 switch (type) {
Jiang Liuc84fced2014-01-07 22:17:10 +0800309 case AARCH64_INSN_IMM_26:
310 mask = BIT(26) - 1;
311 shift = 0;
312 break;
313 case AARCH64_INSN_IMM_19:
314 mask = BIT(19) - 1;
315 shift = 5;
316 break;
317 case AARCH64_INSN_IMM_16:
318 mask = BIT(16) - 1;
319 shift = 5;
320 break;
321 case AARCH64_INSN_IMM_14:
322 mask = BIT(14) - 1;
323 shift = 5;
324 break;
325 case AARCH64_INSN_IMM_12:
326 mask = BIT(12) - 1;
327 shift = 10;
328 break;
329 case AARCH64_INSN_IMM_9:
330 mask = BIT(9) - 1;
331 shift = 12;
332 break;
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100333 case AARCH64_INSN_IMM_7:
334 mask = BIT(7) - 1;
335 shift = 15;
336 break;
Zi Shen Lim5fdc6392014-08-27 05:15:25 +0100337 case AARCH64_INSN_IMM_6:
Zi Shen Lim4a89d2c2014-08-27 05:15:23 +0100338 case AARCH64_INSN_IMM_S:
339 mask = BIT(6) - 1;
340 shift = 10;
341 break;
342 case AARCH64_INSN_IMM_R:
343 mask = BIT(6) - 1;
344 shift = 16;
345 break;
Marc Zyngiera264bf32017-12-03 17:01:39 +0000346 case AARCH64_INSN_IMM_N:
347 mask = 1;
348 shift = 22;
349 break;
Jiang Liuc84fced2014-01-07 22:17:10 +0800350 default:
Marc Zyngier0978fb22015-03-27 13:09:21 +0000351 return -EINVAL;
352 }
353
354 *maskp = mask;
355 *shiftp = shift;
356
357 return 0;
358}
359
360#define ADR_IMM_HILOSPLIT 2
361#define ADR_IMM_SIZE SZ_2M
362#define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1)
363#define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1)
364#define ADR_IMM_LOSHIFT 29
365#define ADR_IMM_HISHIFT 5
366
367u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn)
368{
369 u32 immlo, immhi, mask;
370 int shift;
371
372 switch (type) {
373 case AARCH64_INSN_IMM_ADR:
374 shift = 0;
375 immlo = (insn >> ADR_IMM_LOSHIFT) & ADR_IMM_LOMASK;
376 immhi = (insn >> ADR_IMM_HISHIFT) & ADR_IMM_HIMASK;
377 insn = (immhi << ADR_IMM_HILOSPLIT) | immlo;
378 mask = ADR_IMM_SIZE - 1;
379 break;
380 default:
381 if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
382 pr_err("aarch64_insn_decode_immediate: unknown immediate encoding %d\n",
383 type);
384 return 0;
385 }
386 }
387
388 return (insn >> shift) & mask;
389}
390
391u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
392 u32 insn, u64 imm)
393{
394 u32 immlo, immhi, mask;
395 int shift;
396
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800397 if (insn == AARCH64_BREAK_FAULT)
398 return AARCH64_BREAK_FAULT;
399
Marc Zyngier0978fb22015-03-27 13:09:21 +0000400 switch (type) {
401 case AARCH64_INSN_IMM_ADR:
402 shift = 0;
403 immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT;
404 imm >>= ADR_IMM_HILOSPLIT;
405 immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT;
406 imm = immlo | immhi;
407 mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) |
408 (ADR_IMM_HIMASK << ADR_IMM_HISHIFT));
409 break;
410 default:
411 if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
412 pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n",
413 type);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800414 return AARCH64_BREAK_FAULT;
Marc Zyngier0978fb22015-03-27 13:09:21 +0000415 }
Jiang Liuc84fced2014-01-07 22:17:10 +0800416 }
417
418 /* Update the immediate field. */
419 insn &= ~(mask << shift);
420 insn |= (imm & mask) << shift;
421
422 return insn;
423}
Jiang Liu5c5bf252014-01-07 22:17:11 +0800424
Suzuki K Poulose8c2dcbd2017-01-09 17:28:29 +0000425u32 aarch64_insn_decode_register(enum aarch64_insn_register_type type,
426 u32 insn)
427{
428 int shift;
429
430 switch (type) {
431 case AARCH64_INSN_REGTYPE_RT:
432 case AARCH64_INSN_REGTYPE_RD:
433 shift = 0;
434 break;
435 case AARCH64_INSN_REGTYPE_RN:
436 shift = 5;
437 break;
438 case AARCH64_INSN_REGTYPE_RT2:
439 case AARCH64_INSN_REGTYPE_RA:
440 shift = 10;
441 break;
442 case AARCH64_INSN_REGTYPE_RM:
443 shift = 16;
444 break;
445 default:
446 pr_err("%s: unknown register type encoding %d\n", __func__,
447 type);
448 return 0;
449 }
450
451 return (insn >> shift) & GENMASK(4, 0);
452}
453
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100454static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type,
455 u32 insn,
456 enum aarch64_insn_register reg)
Jiang Liu5c5bf252014-01-07 22:17:11 +0800457{
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100458 int shift;
459
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800460 if (insn == AARCH64_BREAK_FAULT)
461 return AARCH64_BREAK_FAULT;
462
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100463 if (reg < AARCH64_INSN_REG_0 || reg > AARCH64_INSN_REG_SP) {
464 pr_err("%s: unknown register encoding %d\n", __func__, reg);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800465 return AARCH64_BREAK_FAULT;
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100466 }
467
468 switch (type) {
469 case AARCH64_INSN_REGTYPE_RT:
Zi Shen Lim9951a152014-08-27 05:15:22 +0100470 case AARCH64_INSN_REGTYPE_RD:
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100471 shift = 0;
472 break;
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100473 case AARCH64_INSN_REGTYPE_RN:
474 shift = 5;
475 break;
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100476 case AARCH64_INSN_REGTYPE_RT2:
Zi Shen Lim27f95ba2014-08-27 05:15:28 +0100477 case AARCH64_INSN_REGTYPE_RA:
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100478 shift = 10;
479 break;
Zi Shen Lim17cac172014-08-27 05:15:20 +0100480 case AARCH64_INSN_REGTYPE_RM:
Daniel Borkmann85f68fe2017-05-01 02:57:20 +0200481 case AARCH64_INSN_REGTYPE_RS:
Zi Shen Lim17cac172014-08-27 05:15:20 +0100482 shift = 16;
483 break;
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100484 default:
485 pr_err("%s: unknown register type encoding %d\n", __func__,
486 type);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800487 return AARCH64_BREAK_FAULT;
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100488 }
489
490 insn &= ~(GENMASK(4, 0) << shift);
491 insn |= reg << shift;
492
493 return insn;
494}
495
Zi Shen Lim17cac172014-08-27 05:15:20 +0100496static u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type,
497 u32 insn)
498{
499 u32 size;
500
501 switch (type) {
502 case AARCH64_INSN_SIZE_8:
503 size = 0;
504 break;
505 case AARCH64_INSN_SIZE_16:
506 size = 1;
507 break;
508 case AARCH64_INSN_SIZE_32:
509 size = 2;
510 break;
511 case AARCH64_INSN_SIZE_64:
512 size = 3;
513 break;
514 default:
515 pr_err("%s: unknown size encoding %d\n", __func__, type);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800516 return AARCH64_BREAK_FAULT;
Zi Shen Lim17cac172014-08-27 05:15:20 +0100517 }
518
519 insn &= ~GENMASK(31, 30);
520 insn |= size << 30;
521
522 return insn;
523}
524
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100525static inline long branch_imm_common(unsigned long pc, unsigned long addr,
526 long range)
527{
Jiang Liu5c5bf252014-01-07 22:17:11 +0800528 long offset;
529
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800530 if ((pc & 0x3) || (addr & 0x3)) {
531 pr_err("%s: A64 instructions must be word aligned\n", __func__);
532 return range;
533 }
Jiang Liu5c5bf252014-01-07 22:17:11 +0800534
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100535 offset = ((long)addr - (long)pc);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800536
537 if (offset < -range || offset >= range) {
538 pr_err("%s: offset out of range\n", __func__);
539 return range;
540 }
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100541
542 return offset;
543}
544
545u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
546 enum aarch64_insn_branch_type type)
547{
548 u32 insn;
549 long offset;
550
Jiang Liu5c5bf252014-01-07 22:17:11 +0800551 /*
552 * B/BL support [-128M, 128M) offset
553 * ARM64 virtual address arrangement guarantees all kernel and module
554 * texts are within +/-128M.
555 */
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100556 offset = branch_imm_common(pc, addr, SZ_128M);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800557 if (offset >= SZ_128M)
558 return AARCH64_BREAK_FAULT;
Jiang Liu5c5bf252014-01-07 22:17:11 +0800559
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100560 switch (type) {
561 case AARCH64_INSN_BRANCH_LINK:
Jiang Liu5c5bf252014-01-07 22:17:11 +0800562 insn = aarch64_insn_get_bl_value();
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100563 break;
564 case AARCH64_INSN_BRANCH_NOLINK:
Jiang Liu5c5bf252014-01-07 22:17:11 +0800565 insn = aarch64_insn_get_b_value();
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100566 break;
567 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800568 pr_err("%s: unknown branch encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100569 return AARCH64_BREAK_FAULT;
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100570 }
Jiang Liu5c5bf252014-01-07 22:17:11 +0800571
572 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
573 offset >> 2);
574}
575
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100576u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
577 enum aarch64_insn_register reg,
578 enum aarch64_insn_variant variant,
579 enum aarch64_insn_branch_type type)
580{
581 u32 insn;
582 long offset;
583
584 offset = branch_imm_common(pc, addr, SZ_1M);
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800585 if (offset >= SZ_1M)
586 return AARCH64_BREAK_FAULT;
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100587
588 switch (type) {
589 case AARCH64_INSN_BRANCH_COMP_ZERO:
590 insn = aarch64_insn_get_cbz_value();
591 break;
592 case AARCH64_INSN_BRANCH_COMP_NONZERO:
593 insn = aarch64_insn_get_cbnz_value();
594 break;
595 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800596 pr_err("%s: unknown branch encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100597 return AARCH64_BREAK_FAULT;
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100598 }
599
600 switch (variant) {
601 case AARCH64_INSN_VARIANT_32BIT:
602 break;
603 case AARCH64_INSN_VARIANT_64BIT:
604 insn |= AARCH64_INSN_SF_BIT;
605 break;
606 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800607 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +0100608 return AARCH64_BREAK_FAULT;
Zi Shen Lim617d2fb2014-08-27 05:15:17 +0100609 }
610
611 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
612
613 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
614 offset >> 2);
615}
616
Zi Shen Lim345e0d32014-08-27 05:15:19 +0100617u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
618 enum aarch64_insn_condition cond)
619{
620 u32 insn;
621 long offset;
622
623 offset = branch_imm_common(pc, addr, SZ_1M);
624
625 insn = aarch64_insn_get_bcond_value();
626
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800627 if (cond < AARCH64_INSN_COND_EQ || cond > AARCH64_INSN_COND_AL) {
628 pr_err("%s: unknown condition encoding %d\n", __func__, cond);
629 return AARCH64_BREAK_FAULT;
630 }
Zi Shen Lim345e0d32014-08-27 05:15:19 +0100631 insn |= cond;
632
633 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
634 offset >> 2);
635}
636
Jiang Liu5c5bf252014-01-07 22:17:11 +0800637u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op)
638{
639 return aarch64_insn_get_hint_value() | op;
640}
641
642u32 __kprobes aarch64_insn_gen_nop(void)
643{
644 return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP);
645}
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100646
647u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg,
648 enum aarch64_insn_branch_type type)
649{
650 u32 insn;
651
652 switch (type) {
653 case AARCH64_INSN_BRANCH_NOLINK:
654 insn = aarch64_insn_get_br_value();
655 break;
656 case AARCH64_INSN_BRANCH_LINK:
657 insn = aarch64_insn_get_blr_value();
658 break;
659 case AARCH64_INSN_BRANCH_RETURN:
660 insn = aarch64_insn_get_ret_value();
661 break;
662 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800663 pr_err("%s: unknown branch encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100664 return AARCH64_BREAK_FAULT;
Zi Shen Limc0cafba2014-08-27 05:15:18 +0100665 }
666
667 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, reg);
668}
Zi Shen Lim17cac172014-08-27 05:15:20 +0100669
670u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg,
671 enum aarch64_insn_register base,
672 enum aarch64_insn_register offset,
673 enum aarch64_insn_size_type size,
674 enum aarch64_insn_ldst_type type)
675{
676 u32 insn;
677
678 switch (type) {
679 case AARCH64_INSN_LDST_LOAD_REG_OFFSET:
680 insn = aarch64_insn_get_ldr_reg_value();
681 break;
682 case AARCH64_INSN_LDST_STORE_REG_OFFSET:
683 insn = aarch64_insn_get_str_reg_value();
684 break;
685 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800686 pr_err("%s: unknown load/store encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100687 return AARCH64_BREAK_FAULT;
Zi Shen Lim17cac172014-08-27 05:15:20 +0100688 }
689
690 insn = aarch64_insn_encode_ldst_size(size, insn);
691
692 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
693
694 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
695 base);
696
697 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn,
698 offset);
699}
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100700
701u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,
702 enum aarch64_insn_register reg2,
703 enum aarch64_insn_register base,
704 int offset,
705 enum aarch64_insn_variant variant,
706 enum aarch64_insn_ldst_type type)
707{
708 u32 insn;
709 int shift;
710
711 switch (type) {
712 case AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX:
713 insn = aarch64_insn_get_ldp_pre_value();
714 break;
715 case AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX:
716 insn = aarch64_insn_get_stp_pre_value();
717 break;
718 case AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX:
719 insn = aarch64_insn_get_ldp_post_value();
720 break;
721 case AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX:
722 insn = aarch64_insn_get_stp_post_value();
723 break;
724 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800725 pr_err("%s: unknown load/store encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100726 return AARCH64_BREAK_FAULT;
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100727 }
728
729 switch (variant) {
730 case AARCH64_INSN_VARIANT_32BIT:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800731 if ((offset & 0x3) || (offset < -256) || (offset > 252)) {
732 pr_err("%s: offset must be multiples of 4 in the range of [-256, 252] %d\n",
733 __func__, offset);
734 return AARCH64_BREAK_FAULT;
735 }
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100736 shift = 2;
737 break;
738 case AARCH64_INSN_VARIANT_64BIT:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800739 if ((offset & 0x7) || (offset < -512) || (offset > 504)) {
740 pr_err("%s: offset must be multiples of 8 in the range of [-512, 504] %d\n",
741 __func__, offset);
742 return AARCH64_BREAK_FAULT;
743 }
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100744 shift = 3;
745 insn |= AARCH64_INSN_SF_BIT;
746 break;
747 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800748 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +0100749 return AARCH64_BREAK_FAULT;
Zi Shen Lim1bba5672014-08-27 05:15:21 +0100750 }
751
752 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn,
753 reg1);
754
755 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn,
756 reg2);
757
758 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
759 base);
760
761 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_7, insn,
762 offset >> shift);
763}
Zi Shen Lim9951a152014-08-27 05:15:22 +0100764
Daniel Borkmann85f68fe2017-05-01 02:57:20 +0200765u32 aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg,
766 enum aarch64_insn_register base,
767 enum aarch64_insn_register state,
768 enum aarch64_insn_size_type size,
769 enum aarch64_insn_ldst_type type)
770{
771 u32 insn;
772
773 switch (type) {
774 case AARCH64_INSN_LDST_LOAD_EX:
775 insn = aarch64_insn_get_load_ex_value();
776 break;
777 case AARCH64_INSN_LDST_STORE_EX:
778 insn = aarch64_insn_get_store_ex_value();
779 break;
780 default:
781 pr_err("%s: unknown load/store exclusive encoding %d\n", __func__, type);
782 return AARCH64_BREAK_FAULT;
783 }
784
785 insn = aarch64_insn_encode_ldst_size(size, insn);
786
787 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn,
788 reg);
789
790 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
791 base);
792
793 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn,
794 AARCH64_INSN_REG_ZR);
795
796 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn,
797 state);
798}
799
800static u32 aarch64_insn_encode_prfm_imm(enum aarch64_insn_prfm_type type,
801 enum aarch64_insn_prfm_target target,
802 enum aarch64_insn_prfm_policy policy,
803 u32 insn)
804{
805 u32 imm_type = 0, imm_target = 0, imm_policy = 0;
806
807 switch (type) {
808 case AARCH64_INSN_PRFM_TYPE_PLD:
809 break;
810 case AARCH64_INSN_PRFM_TYPE_PLI:
811 imm_type = BIT(0);
812 break;
813 case AARCH64_INSN_PRFM_TYPE_PST:
814 imm_type = BIT(1);
815 break;
816 default:
817 pr_err("%s: unknown prfm type encoding %d\n", __func__, type);
818 return AARCH64_BREAK_FAULT;
819 }
820
821 switch (target) {
822 case AARCH64_INSN_PRFM_TARGET_L1:
823 break;
824 case AARCH64_INSN_PRFM_TARGET_L2:
825 imm_target = BIT(0);
826 break;
827 case AARCH64_INSN_PRFM_TARGET_L3:
828 imm_target = BIT(1);
829 break;
830 default:
831 pr_err("%s: unknown prfm target encoding %d\n", __func__, target);
832 return AARCH64_BREAK_FAULT;
833 }
834
835 switch (policy) {
836 case AARCH64_INSN_PRFM_POLICY_KEEP:
837 break;
838 case AARCH64_INSN_PRFM_POLICY_STRM:
839 imm_policy = BIT(0);
840 break;
841 default:
842 pr_err("%s: unknown prfm policy encoding %d\n", __func__, policy);
843 return AARCH64_BREAK_FAULT;
844 }
845
846 /* In this case, imm5 is encoded into Rt field. */
847 insn &= ~GENMASK(4, 0);
848 insn |= imm_policy | (imm_target << 1) | (imm_type << 3);
849
850 return insn;
851}
852
853u32 aarch64_insn_gen_prefetch(enum aarch64_insn_register base,
854 enum aarch64_insn_prfm_type type,
855 enum aarch64_insn_prfm_target target,
856 enum aarch64_insn_prfm_policy policy)
857{
858 u32 insn = aarch64_insn_get_prfm_value();
859
860 insn = aarch64_insn_encode_ldst_size(AARCH64_INSN_SIZE_64, insn);
861
862 insn = aarch64_insn_encode_prfm_imm(type, target, policy, insn);
863
864 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
865 base);
866
867 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, 0);
868}
869
Zi Shen Lim9951a152014-08-27 05:15:22 +0100870u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst,
871 enum aarch64_insn_register src,
872 int imm, enum aarch64_insn_variant variant,
873 enum aarch64_insn_adsb_type type)
874{
875 u32 insn;
876
877 switch (type) {
878 case AARCH64_INSN_ADSB_ADD:
879 insn = aarch64_insn_get_add_imm_value();
880 break;
881 case AARCH64_INSN_ADSB_SUB:
882 insn = aarch64_insn_get_sub_imm_value();
883 break;
884 case AARCH64_INSN_ADSB_ADD_SETFLAGS:
885 insn = aarch64_insn_get_adds_imm_value();
886 break;
887 case AARCH64_INSN_ADSB_SUB_SETFLAGS:
888 insn = aarch64_insn_get_subs_imm_value();
889 break;
890 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800891 pr_err("%s: unknown add/sub encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100892 return AARCH64_BREAK_FAULT;
Zi Shen Lim9951a152014-08-27 05:15:22 +0100893 }
894
895 switch (variant) {
896 case AARCH64_INSN_VARIANT_32BIT:
897 break;
898 case AARCH64_INSN_VARIANT_64BIT:
899 insn |= AARCH64_INSN_SF_BIT;
900 break;
901 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800902 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +0100903 return AARCH64_BREAK_FAULT;
Zi Shen Lim9951a152014-08-27 05:15:22 +0100904 }
905
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800906 if (imm & ~(SZ_4K - 1)) {
907 pr_err("%s: invalid immediate encoding %d\n", __func__, imm);
908 return AARCH64_BREAK_FAULT;
909 }
Zi Shen Lim9951a152014-08-27 05:15:22 +0100910
911 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
912
913 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
914
915 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm);
916}
Zi Shen Lim4a89d2c2014-08-27 05:15:23 +0100917
918u32 aarch64_insn_gen_bitfield(enum aarch64_insn_register dst,
919 enum aarch64_insn_register src,
920 int immr, int imms,
921 enum aarch64_insn_variant variant,
922 enum aarch64_insn_bitfield_type type)
923{
924 u32 insn;
925 u32 mask;
926
927 switch (type) {
928 case AARCH64_INSN_BITFIELD_MOVE:
929 insn = aarch64_insn_get_bfm_value();
930 break;
931 case AARCH64_INSN_BITFIELD_MOVE_UNSIGNED:
932 insn = aarch64_insn_get_ubfm_value();
933 break;
934 case AARCH64_INSN_BITFIELD_MOVE_SIGNED:
935 insn = aarch64_insn_get_sbfm_value();
936 break;
937 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800938 pr_err("%s: unknown bitfield encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100939 return AARCH64_BREAK_FAULT;
Zi Shen Lim4a89d2c2014-08-27 05:15:23 +0100940 }
941
942 switch (variant) {
943 case AARCH64_INSN_VARIANT_32BIT:
944 mask = GENMASK(4, 0);
945 break;
946 case AARCH64_INSN_VARIANT_64BIT:
947 insn |= AARCH64_INSN_SF_BIT | AARCH64_INSN_N_BIT;
948 mask = GENMASK(5, 0);
949 break;
950 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800951 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +0100952 return AARCH64_BREAK_FAULT;
Zi Shen Lim4a89d2c2014-08-27 05:15:23 +0100953 }
954
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800955 if (immr & ~mask) {
956 pr_err("%s: invalid immr encoding %d\n", __func__, immr);
957 return AARCH64_BREAK_FAULT;
958 }
959 if (imms & ~mask) {
960 pr_err("%s: invalid imms encoding %d\n", __func__, imms);
961 return AARCH64_BREAK_FAULT;
962 }
Zi Shen Lim4a89d2c2014-08-27 05:15:23 +0100963
964 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
965
966 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
967
968 insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr);
969
970 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms);
971}
Zi Shen Lim6098f2d2014-08-27 05:15:24 +0100972
973u32 aarch64_insn_gen_movewide(enum aarch64_insn_register dst,
974 int imm, int shift,
975 enum aarch64_insn_variant variant,
976 enum aarch64_insn_movewide_type type)
977{
978 u32 insn;
979
980 switch (type) {
981 case AARCH64_INSN_MOVEWIDE_ZERO:
982 insn = aarch64_insn_get_movz_value();
983 break;
984 case AARCH64_INSN_MOVEWIDE_KEEP:
985 insn = aarch64_insn_get_movk_value();
986 break;
987 case AARCH64_INSN_MOVEWIDE_INVERSE:
988 insn = aarch64_insn_get_movn_value();
989 break;
990 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800991 pr_err("%s: unknown movewide encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +0100992 return AARCH64_BREAK_FAULT;
Zi Shen Lim6098f2d2014-08-27 05:15:24 +0100993 }
994
Zi Shen Limc94ae4f2016-01-13 23:33:21 -0800995 if (imm & ~(SZ_64K - 1)) {
996 pr_err("%s: invalid immediate encoding %d\n", __func__, imm);
997 return AARCH64_BREAK_FAULT;
998 }
Zi Shen Lim6098f2d2014-08-27 05:15:24 +0100999
1000 switch (variant) {
1001 case AARCH64_INSN_VARIANT_32BIT:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001002 if (shift != 0 && shift != 16) {
1003 pr_err("%s: invalid shift encoding %d\n", __func__,
1004 shift);
1005 return AARCH64_BREAK_FAULT;
1006 }
Zi Shen Lim6098f2d2014-08-27 05:15:24 +01001007 break;
1008 case AARCH64_INSN_VARIANT_64BIT:
1009 insn |= AARCH64_INSN_SF_BIT;
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001010 if (shift != 0 && shift != 16 && shift != 32 && shift != 48) {
1011 pr_err("%s: invalid shift encoding %d\n", __func__,
1012 shift);
1013 return AARCH64_BREAK_FAULT;
1014 }
Zi Shen Lim6098f2d2014-08-27 05:15:24 +01001015 break;
1016 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001017 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +01001018 return AARCH64_BREAK_FAULT;
Zi Shen Lim6098f2d2014-08-27 05:15:24 +01001019 }
1020
1021 insn |= (shift >> 4) << 21;
1022
1023 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
1024
1025 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm);
1026}
Zi Shen Lim5fdc6392014-08-27 05:15:25 +01001027
1028u32 aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst,
1029 enum aarch64_insn_register src,
1030 enum aarch64_insn_register reg,
1031 int shift,
1032 enum aarch64_insn_variant variant,
1033 enum aarch64_insn_adsb_type type)
1034{
1035 u32 insn;
1036
1037 switch (type) {
1038 case AARCH64_INSN_ADSB_ADD:
1039 insn = aarch64_insn_get_add_value();
1040 break;
1041 case AARCH64_INSN_ADSB_SUB:
1042 insn = aarch64_insn_get_sub_value();
1043 break;
1044 case AARCH64_INSN_ADSB_ADD_SETFLAGS:
1045 insn = aarch64_insn_get_adds_value();
1046 break;
1047 case AARCH64_INSN_ADSB_SUB_SETFLAGS:
1048 insn = aarch64_insn_get_subs_value();
1049 break;
1050 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001051 pr_err("%s: unknown add/sub encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +01001052 return AARCH64_BREAK_FAULT;
Zi Shen Lim5fdc6392014-08-27 05:15:25 +01001053 }
1054
1055 switch (variant) {
1056 case AARCH64_INSN_VARIANT_32BIT:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001057 if (shift & ~(SZ_32 - 1)) {
1058 pr_err("%s: invalid shift encoding %d\n", __func__,
1059 shift);
1060 return AARCH64_BREAK_FAULT;
1061 }
Zi Shen Lim5fdc6392014-08-27 05:15:25 +01001062 break;
1063 case AARCH64_INSN_VARIANT_64BIT:
1064 insn |= AARCH64_INSN_SF_BIT;
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001065 if (shift & ~(SZ_64 - 1)) {
1066 pr_err("%s: invalid shift encoding %d\n", __func__,
1067 shift);
1068 return AARCH64_BREAK_FAULT;
1069 }
Zi Shen Lim5fdc6392014-08-27 05:15:25 +01001070 break;
1071 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001072 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +01001073 return AARCH64_BREAK_FAULT;
Zi Shen Lim5fdc6392014-08-27 05:15:25 +01001074 }
1075
1076
1077 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
1078
1079 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
1080
1081 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
1082
1083 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
1084}
Zi Shen Lim546dd362014-08-27 05:15:26 +01001085
1086u32 aarch64_insn_gen_data1(enum aarch64_insn_register dst,
1087 enum aarch64_insn_register src,
1088 enum aarch64_insn_variant variant,
1089 enum aarch64_insn_data1_type type)
1090{
1091 u32 insn;
1092
1093 switch (type) {
1094 case AARCH64_INSN_DATA1_REVERSE_16:
1095 insn = aarch64_insn_get_rev16_value();
1096 break;
1097 case AARCH64_INSN_DATA1_REVERSE_32:
1098 insn = aarch64_insn_get_rev32_value();
1099 break;
1100 case AARCH64_INSN_DATA1_REVERSE_64:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001101 if (variant != AARCH64_INSN_VARIANT_64BIT) {
1102 pr_err("%s: invalid variant for reverse64 %d\n",
1103 __func__, variant);
1104 return AARCH64_BREAK_FAULT;
1105 }
Zi Shen Lim546dd362014-08-27 05:15:26 +01001106 insn = aarch64_insn_get_rev64_value();
1107 break;
1108 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001109 pr_err("%s: unknown data1 encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +01001110 return AARCH64_BREAK_FAULT;
Zi Shen Lim546dd362014-08-27 05:15:26 +01001111 }
1112
1113 switch (variant) {
1114 case AARCH64_INSN_VARIANT_32BIT:
1115 break;
1116 case AARCH64_INSN_VARIANT_64BIT:
1117 insn |= AARCH64_INSN_SF_BIT;
1118 break;
1119 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001120 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +01001121 return AARCH64_BREAK_FAULT;
Zi Shen Lim546dd362014-08-27 05:15:26 +01001122 }
1123
1124 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
1125
1126 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
1127}
Zi Shen Lim64810632014-08-27 05:15:27 +01001128
1129u32 aarch64_insn_gen_data2(enum aarch64_insn_register dst,
1130 enum aarch64_insn_register src,
1131 enum aarch64_insn_register reg,
1132 enum aarch64_insn_variant variant,
1133 enum aarch64_insn_data2_type type)
1134{
1135 u32 insn;
1136
1137 switch (type) {
1138 case AARCH64_INSN_DATA2_UDIV:
1139 insn = aarch64_insn_get_udiv_value();
1140 break;
1141 case AARCH64_INSN_DATA2_SDIV:
1142 insn = aarch64_insn_get_sdiv_value();
1143 break;
1144 case AARCH64_INSN_DATA2_LSLV:
1145 insn = aarch64_insn_get_lslv_value();
1146 break;
1147 case AARCH64_INSN_DATA2_LSRV:
1148 insn = aarch64_insn_get_lsrv_value();
1149 break;
1150 case AARCH64_INSN_DATA2_ASRV:
1151 insn = aarch64_insn_get_asrv_value();
1152 break;
1153 case AARCH64_INSN_DATA2_RORV:
1154 insn = aarch64_insn_get_rorv_value();
1155 break;
1156 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001157 pr_err("%s: unknown data2 encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +01001158 return AARCH64_BREAK_FAULT;
Zi Shen Lim64810632014-08-27 05:15:27 +01001159 }
1160
1161 switch (variant) {
1162 case AARCH64_INSN_VARIANT_32BIT:
1163 break;
1164 case AARCH64_INSN_VARIANT_64BIT:
1165 insn |= AARCH64_INSN_SF_BIT;
1166 break;
1167 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001168 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +01001169 return AARCH64_BREAK_FAULT;
Zi Shen Lim64810632014-08-27 05:15:27 +01001170 }
1171
1172 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
1173
1174 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
1175
1176 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
1177}
Zi Shen Lim27f95ba2014-08-27 05:15:28 +01001178
1179u32 aarch64_insn_gen_data3(enum aarch64_insn_register dst,
1180 enum aarch64_insn_register src,
1181 enum aarch64_insn_register reg1,
1182 enum aarch64_insn_register reg2,
1183 enum aarch64_insn_variant variant,
1184 enum aarch64_insn_data3_type type)
1185{
1186 u32 insn;
1187
1188 switch (type) {
1189 case AARCH64_INSN_DATA3_MADD:
1190 insn = aarch64_insn_get_madd_value();
1191 break;
1192 case AARCH64_INSN_DATA3_MSUB:
1193 insn = aarch64_insn_get_msub_value();
1194 break;
1195 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001196 pr_err("%s: unknown data3 encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +01001197 return AARCH64_BREAK_FAULT;
Zi Shen Lim27f95ba2014-08-27 05:15:28 +01001198 }
1199
1200 switch (variant) {
1201 case AARCH64_INSN_VARIANT_32BIT:
1202 break;
1203 case AARCH64_INSN_VARIANT_64BIT:
1204 insn |= AARCH64_INSN_SF_BIT;
1205 break;
1206 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001207 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +01001208 return AARCH64_BREAK_FAULT;
Zi Shen Lim27f95ba2014-08-27 05:15:28 +01001209 }
1210
1211 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
1212
1213 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RA, insn, src);
1214
1215 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
1216 reg1);
1217
1218 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn,
1219 reg2);
1220}
Zi Shen Lim5e6e15a2014-08-27 05:15:29 +01001221
1222u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,
1223 enum aarch64_insn_register src,
1224 enum aarch64_insn_register reg,
1225 int shift,
1226 enum aarch64_insn_variant variant,
1227 enum aarch64_insn_logic_type type)
1228{
1229 u32 insn;
1230
1231 switch (type) {
1232 case AARCH64_INSN_LOGIC_AND:
1233 insn = aarch64_insn_get_and_value();
1234 break;
1235 case AARCH64_INSN_LOGIC_BIC:
1236 insn = aarch64_insn_get_bic_value();
1237 break;
1238 case AARCH64_INSN_LOGIC_ORR:
1239 insn = aarch64_insn_get_orr_value();
1240 break;
1241 case AARCH64_INSN_LOGIC_ORN:
1242 insn = aarch64_insn_get_orn_value();
1243 break;
1244 case AARCH64_INSN_LOGIC_EOR:
1245 insn = aarch64_insn_get_eor_value();
1246 break;
1247 case AARCH64_INSN_LOGIC_EON:
1248 insn = aarch64_insn_get_eon_value();
1249 break;
1250 case AARCH64_INSN_LOGIC_AND_SETFLAGS:
1251 insn = aarch64_insn_get_ands_value();
1252 break;
1253 case AARCH64_INSN_LOGIC_BIC_SETFLAGS:
1254 insn = aarch64_insn_get_bics_value();
1255 break;
1256 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001257 pr_err("%s: unknown logical encoding %d\n", __func__, type);
Mark Browna9ae04c2014-09-16 17:42:33 +01001258 return AARCH64_BREAK_FAULT;
Zi Shen Lim5e6e15a2014-08-27 05:15:29 +01001259 }
1260
1261 switch (variant) {
1262 case AARCH64_INSN_VARIANT_32BIT:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001263 if (shift & ~(SZ_32 - 1)) {
1264 pr_err("%s: invalid shift encoding %d\n", __func__,
1265 shift);
1266 return AARCH64_BREAK_FAULT;
1267 }
Zi Shen Lim5e6e15a2014-08-27 05:15:29 +01001268 break;
1269 case AARCH64_INSN_VARIANT_64BIT:
1270 insn |= AARCH64_INSN_SF_BIT;
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001271 if (shift & ~(SZ_64 - 1)) {
1272 pr_err("%s: invalid shift encoding %d\n", __func__,
1273 shift);
1274 return AARCH64_BREAK_FAULT;
1275 }
Zi Shen Lim5e6e15a2014-08-27 05:15:29 +01001276 break;
1277 default:
Zi Shen Limc94ae4f2016-01-13 23:33:21 -08001278 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
Mark Browna9ae04c2014-09-16 17:42:33 +01001279 return AARCH64_BREAK_FAULT;
Zi Shen Lim5e6e15a2014-08-27 05:15:29 +01001280 }
1281
1282
1283 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
1284
1285 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
1286
1287 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
1288
1289 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
1290}
Punit Agrawal9b79f522014-11-18 11:41:22 +00001291
Marc Zyngier10b48f72015-06-01 10:47:39 +01001292/*
1293 * Decode the imm field of a branch, and return the byte offset as a
1294 * signed value (so it can be used when computing a new branch
1295 * target).
1296 */
1297s32 aarch64_get_branch_offset(u32 insn)
1298{
1299 s32 imm;
1300
1301 if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) {
1302 imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn);
1303 return (imm << 6) >> 4;
1304 }
1305
1306 if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
1307 aarch64_insn_is_bcond(insn)) {
1308 imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_19, insn);
1309 return (imm << 13) >> 11;
1310 }
1311
1312 if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) {
1313 imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_14, insn);
1314 return (imm << 18) >> 16;
1315 }
1316
1317 /* Unhandled instruction */
1318 BUG();
1319}
1320
1321/*
1322 * Encode the displacement of a branch in the imm field and return the
1323 * updated instruction.
1324 */
1325u32 aarch64_set_branch_offset(u32 insn, s32 offset)
1326{
1327 if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn))
1328 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
1329 offset >> 2);
1330
1331 if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
1332 aarch64_insn_is_bcond(insn))
1333 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
1334 offset >> 2);
1335
1336 if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn))
1337 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_14, insn,
1338 offset >> 2);
1339
1340 /* Unhandled instruction */
1341 BUG();
1342}
1343
Suzuki K Poulose46084bc2016-09-09 14:07:12 +01001344s32 aarch64_insn_adrp_get_offset(u32 insn)
1345{
1346 BUG_ON(!aarch64_insn_is_adrp(insn));
1347 return aarch64_insn_decode_immediate(AARCH64_INSN_IMM_ADR, insn) << 12;
1348}
1349
1350u32 aarch64_insn_adrp_set_offset(u32 insn, s32 offset)
1351{
1352 BUG_ON(!aarch64_insn_is_adrp(insn));
1353 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_ADR, insn,
1354 offset >> 12);
1355}
1356
David A. Longd59bee82016-07-08 12:35:46 -04001357/*
1358 * Extract the Op/CR data from a msr/mrs instruction.
1359 */
1360u32 aarch64_insn_extract_system_reg(u32 insn)
1361{
1362 return (insn & 0x1FFFE0) >> 5;
1363}
1364
Punit Agrawal9b79f522014-11-18 11:41:22 +00001365bool aarch32_insn_is_wide(u32 insn)
1366{
1367 return insn >= 0xe800;
1368}
Punit Agrawalbd35a4a2014-11-18 11:41:25 +00001369
1370/*
1371 * Macros/defines for extracting register numbers from instruction.
1372 */
1373u32 aarch32_insn_extract_reg_num(u32 insn, int offset)
1374{
1375 return (insn & (0xf << offset)) >> offset;
1376}
Punit Agrawalc852f322014-11-18 11:41:26 +00001377
1378#define OPC2_MASK 0x7
1379#define OPC2_OFFSET 5
1380u32 aarch32_insn_mcr_extract_opc2(u32 insn)
1381{
1382 return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET;
1383}
1384
1385#define CRM_MASK 0xf
1386u32 aarch32_insn_mcr_extract_crm(u32 insn)
1387{
1388 return insn & CRM_MASK;
1389}
David A. Long2af3ec02016-07-08 12:35:47 -04001390
1391static bool __kprobes __check_eq(unsigned long pstate)
1392{
1393 return (pstate & PSR_Z_BIT) != 0;
1394}
1395
1396static bool __kprobes __check_ne(unsigned long pstate)
1397{
1398 return (pstate & PSR_Z_BIT) == 0;
1399}
1400
1401static bool __kprobes __check_cs(unsigned long pstate)
1402{
1403 return (pstate & PSR_C_BIT) != 0;
1404}
1405
1406static bool __kprobes __check_cc(unsigned long pstate)
1407{
1408 return (pstate & PSR_C_BIT) == 0;
1409}
1410
1411static bool __kprobes __check_mi(unsigned long pstate)
1412{
1413 return (pstate & PSR_N_BIT) != 0;
1414}
1415
1416static bool __kprobes __check_pl(unsigned long pstate)
1417{
1418 return (pstate & PSR_N_BIT) == 0;
1419}
1420
1421static bool __kprobes __check_vs(unsigned long pstate)
1422{
1423 return (pstate & PSR_V_BIT) != 0;
1424}
1425
1426static bool __kprobes __check_vc(unsigned long pstate)
1427{
1428 return (pstate & PSR_V_BIT) == 0;
1429}
1430
1431static bool __kprobes __check_hi(unsigned long pstate)
1432{
1433 pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
1434 return (pstate & PSR_C_BIT) != 0;
1435}
1436
1437static bool __kprobes __check_ls(unsigned long pstate)
1438{
1439 pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
1440 return (pstate & PSR_C_BIT) == 0;
1441}
1442
1443static bool __kprobes __check_ge(unsigned long pstate)
1444{
1445 pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
1446 return (pstate & PSR_N_BIT) == 0;
1447}
1448
1449static bool __kprobes __check_lt(unsigned long pstate)
1450{
1451 pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
1452 return (pstate & PSR_N_BIT) != 0;
1453}
1454
1455static bool __kprobes __check_gt(unsigned long pstate)
1456{
1457 /*PSR_N_BIT ^= PSR_V_BIT */
1458 unsigned long temp = pstate ^ (pstate << 3);
1459
1460 temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
1461 return (temp & PSR_N_BIT) == 0;
1462}
1463
1464static bool __kprobes __check_le(unsigned long pstate)
1465{
1466 /*PSR_N_BIT ^= PSR_V_BIT */
1467 unsigned long temp = pstate ^ (pstate << 3);
1468
1469 temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
1470 return (temp & PSR_N_BIT) != 0;
1471}
1472
1473static bool __kprobes __check_al(unsigned long pstate)
1474{
1475 return true;
1476}
1477
1478/*
1479 * Note that the ARMv8 ARM calls condition code 0b1111 "nv", but states that
1480 * it behaves identically to 0b1110 ("al").
1481 */
1482pstate_check_t * const aarch32_opcode_cond_checks[16] = {
1483 __check_eq, __check_ne, __check_cs, __check_cc,
1484 __check_mi, __check_pl, __check_vs, __check_vc,
1485 __check_hi, __check_ls, __check_ge, __check_lt,
1486 __check_gt, __check_le, __check_al, __check_al
1487};
Marc Zyngieref3935e2017-12-03 17:09:08 +00001488
1489static bool range_of_ones(u64 val)
1490{
1491 /* Doesn't handle full ones or full zeroes */
1492 u64 sval = val >> __ffs64(val);
1493
1494 /* One of Sean Eron Anderson's bithack tricks */
1495 return ((sval + 1) & (sval)) == 0;
1496}
1497
1498static u32 aarch64_encode_immediate(u64 imm,
1499 enum aarch64_insn_variant variant,
1500 u32 insn)
1501{
1502 unsigned int immr, imms, n, ones, ror, esz, tmp;
1503 u64 mask = ~0UL;
1504
1505 /* Can't encode full zeroes or full ones */
1506 if (!imm || !~imm)
1507 return AARCH64_BREAK_FAULT;
1508
1509 switch (variant) {
1510 case AARCH64_INSN_VARIANT_32BIT:
1511 if (upper_32_bits(imm))
1512 return AARCH64_BREAK_FAULT;
1513 esz = 32;
1514 break;
1515 case AARCH64_INSN_VARIANT_64BIT:
1516 insn |= AARCH64_INSN_SF_BIT;
1517 esz = 64;
1518 break;
1519 default:
1520 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
1521 return AARCH64_BREAK_FAULT;
1522 }
1523
1524 /*
1525 * Inverse of Replicate(). Try to spot a repeating pattern
1526 * with a pow2 stride.
1527 */
1528 for (tmp = esz / 2; tmp >= 2; tmp /= 2) {
1529 u64 emask = BIT(tmp) - 1;
1530
1531 if ((imm & emask) != ((imm >> tmp) & emask))
1532 break;
1533
1534 esz = tmp;
1535 mask = emask;
1536 }
1537
1538 /* N is only set if we're encoding a 64bit value */
1539 n = esz == 64;
1540
1541 /* Trim imm to the element size */
1542 imm &= mask;
1543
1544 /* That's how many ones we need to encode */
1545 ones = hweight64(imm);
1546
1547 /*
1548 * imms is set to (ones - 1), prefixed with a string of ones
1549 * and a zero if they fit. Cap it to 6 bits.
1550 */
1551 imms = ones - 1;
1552 imms |= 0xf << ffs(esz);
1553 imms &= BIT(6) - 1;
1554
1555 /* Compute the rotation */
1556 if (range_of_ones(imm)) {
1557 /*
1558 * Pattern: 0..01..10..0
1559 *
1560 * Compute how many rotate we need to align it right
1561 */
1562 ror = __ffs64(imm);
1563 } else {
1564 /*
1565 * Pattern: 0..01..10..01..1
1566 *
1567 * Fill the unused top bits with ones, and check if
1568 * the result is a valid immediate (all ones with a
1569 * contiguous ranges of zeroes).
1570 */
1571 imm |= ~mask;
1572 if (!range_of_ones(~imm))
1573 return AARCH64_BREAK_FAULT;
1574
1575 /*
1576 * Compute the rotation to get a continuous set of
1577 * ones, with the first bit set at position 0
1578 */
1579 ror = fls(~imm);
1580 }
1581
1582 /*
1583 * immr is the number of bits we need to rotate back to the
1584 * original set of ones. Note that this is relative to the
1585 * element size...
1586 */
1587 immr = (esz - ror) % esz;
1588
1589 insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, n);
1590 insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr);
1591 return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms);
1592}
1593
1594u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
1595 enum aarch64_insn_variant variant,
1596 enum aarch64_insn_register Rn,
1597 enum aarch64_insn_register Rd,
1598 u64 imm)
1599{
1600 u32 insn;
1601
1602 switch (type) {
1603 case AARCH64_INSN_LOGIC_AND:
1604 insn = aarch64_insn_get_and_imm_value();
1605 break;
1606 case AARCH64_INSN_LOGIC_ORR:
1607 insn = aarch64_insn_get_orr_imm_value();
1608 break;
1609 case AARCH64_INSN_LOGIC_EOR:
1610 insn = aarch64_insn_get_eor_imm_value();
1611 break;
1612 case AARCH64_INSN_LOGIC_AND_SETFLAGS:
1613 insn = aarch64_insn_get_ands_imm_value();
1614 break;
1615 default:
1616 pr_err("%s: unknown logical encoding %d\n", __func__, type);
1617 return AARCH64_BREAK_FAULT;
1618 }
1619
1620 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd);
1621 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
1622 return aarch64_encode_immediate(imm, variant, insn);
1623}
Marc Zyngier9f2efa32017-12-03 17:47:03 +00001624
1625u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant,
1626 enum aarch64_insn_register Rm,
1627 enum aarch64_insn_register Rn,
1628 enum aarch64_insn_register Rd,
1629 u8 lsb)
1630{
1631 u32 insn;
1632
1633 insn = aarch64_insn_get_extr_value();
1634
1635 switch (variant) {
1636 case AARCH64_INSN_VARIANT_32BIT:
1637 if (lsb > 31)
1638 return AARCH64_BREAK_FAULT;
1639 break;
1640 case AARCH64_INSN_VARIANT_64BIT:
1641 if (lsb > 63)
1642 return AARCH64_BREAK_FAULT;
1643 insn |= AARCH64_INSN_SF_BIT;
1644 insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, 1);
1645 break;
1646 default:
1647 pr_err("%s: unknown variant encoding %d\n", __func__, variant);
1648 return AARCH64_BREAK_FAULT;
1649 }
1650
1651 insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, lsb);
1652 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd);
1653 insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
1654 return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, Rm);
1655}