xref: /openbmc/linux/arch/arm64/lib/insn.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
172fd7236SJulien Thierry // SPDX-License-Identifier: GPL-2.0-only
272fd7236SJulien Thierry /*
372fd7236SJulien Thierry  * Copyright (C) 2013 Huawei Ltd.
472fd7236SJulien Thierry  * Author: Jiang Liu <liuj97@gmail.com>
572fd7236SJulien Thierry  *
672fd7236SJulien Thierry  * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com>
772fd7236SJulien Thierry  */
872fd7236SJulien Thierry #include <linux/bitops.h>
972fd7236SJulien Thierry #include <linux/bug.h>
1072fd7236SJulien Thierry #include <linux/printk.h>
1172fd7236SJulien Thierry #include <linux/sizes.h>
1272fd7236SJulien Thierry #include <linux/types.h>
1372fd7236SJulien Thierry 
1472fd7236SJulien Thierry #include <asm/debug-monitors.h>
1572fd7236SJulien Thierry #include <asm/errno.h>
1672fd7236SJulien Thierry #include <asm/insn.h>
1772fd7236SJulien Thierry #include <asm/kprobes.h>
1872fd7236SJulien Thierry 
1972fd7236SJulien Thierry #define AARCH64_INSN_SF_BIT	BIT(31)
2072fd7236SJulien Thierry #define AARCH64_INSN_N_BIT	BIT(22)
2172fd7236SJulien Thierry #define AARCH64_INSN_LSL_12	BIT(22)
2272fd7236SJulien Thierry 
aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type,u32 * maskp,int * shiftp)2372fd7236SJulien Thierry static int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type,
2472fd7236SJulien Thierry 						u32 *maskp, int *shiftp)
2572fd7236SJulien Thierry {
2672fd7236SJulien Thierry 	u32 mask;
2772fd7236SJulien Thierry 	int shift;
2872fd7236SJulien Thierry 
2972fd7236SJulien Thierry 	switch (type) {
3072fd7236SJulien Thierry 	case AARCH64_INSN_IMM_26:
3172fd7236SJulien Thierry 		mask = BIT(26) - 1;
3272fd7236SJulien Thierry 		shift = 0;
3372fd7236SJulien Thierry 		break;
3472fd7236SJulien Thierry 	case AARCH64_INSN_IMM_19:
3572fd7236SJulien Thierry 		mask = BIT(19) - 1;
3672fd7236SJulien Thierry 		shift = 5;
3772fd7236SJulien Thierry 		break;
3872fd7236SJulien Thierry 	case AARCH64_INSN_IMM_16:
3972fd7236SJulien Thierry 		mask = BIT(16) - 1;
4072fd7236SJulien Thierry 		shift = 5;
4172fd7236SJulien Thierry 		break;
4272fd7236SJulien Thierry 	case AARCH64_INSN_IMM_14:
4372fd7236SJulien Thierry 		mask = BIT(14) - 1;
4472fd7236SJulien Thierry 		shift = 5;
4572fd7236SJulien Thierry 		break;
4672fd7236SJulien Thierry 	case AARCH64_INSN_IMM_12:
4772fd7236SJulien Thierry 		mask = BIT(12) - 1;
4872fd7236SJulien Thierry 		shift = 10;
4972fd7236SJulien Thierry 		break;
5072fd7236SJulien Thierry 	case AARCH64_INSN_IMM_9:
5172fd7236SJulien Thierry 		mask = BIT(9) - 1;
5272fd7236SJulien Thierry 		shift = 12;
5372fd7236SJulien Thierry 		break;
5472fd7236SJulien Thierry 	case AARCH64_INSN_IMM_7:
5572fd7236SJulien Thierry 		mask = BIT(7) - 1;
5672fd7236SJulien Thierry 		shift = 15;
5772fd7236SJulien Thierry 		break;
5872fd7236SJulien Thierry 	case AARCH64_INSN_IMM_6:
5972fd7236SJulien Thierry 	case AARCH64_INSN_IMM_S:
6072fd7236SJulien Thierry 		mask = BIT(6) - 1;
6172fd7236SJulien Thierry 		shift = 10;
6272fd7236SJulien Thierry 		break;
6372fd7236SJulien Thierry 	case AARCH64_INSN_IMM_R:
6472fd7236SJulien Thierry 		mask = BIT(6) - 1;
6572fd7236SJulien Thierry 		shift = 16;
6672fd7236SJulien Thierry 		break;
6772fd7236SJulien Thierry 	case AARCH64_INSN_IMM_N:
6872fd7236SJulien Thierry 		mask = 1;
6972fd7236SJulien Thierry 		shift = 22;
7072fd7236SJulien Thierry 		break;
7172fd7236SJulien Thierry 	default:
7272fd7236SJulien Thierry 		return -EINVAL;
7372fd7236SJulien Thierry 	}
7472fd7236SJulien Thierry 
7572fd7236SJulien Thierry 	*maskp = mask;
7672fd7236SJulien Thierry 	*shiftp = shift;
7772fd7236SJulien Thierry 
7872fd7236SJulien Thierry 	return 0;
7972fd7236SJulien Thierry }
8072fd7236SJulien Thierry 
8172fd7236SJulien Thierry #define ADR_IMM_HILOSPLIT	2
8272fd7236SJulien Thierry #define ADR_IMM_SIZE		SZ_2M
8372fd7236SJulien Thierry #define ADR_IMM_LOMASK		((1 << ADR_IMM_HILOSPLIT) - 1)
8472fd7236SJulien Thierry #define ADR_IMM_HIMASK		((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1)
8572fd7236SJulien Thierry #define ADR_IMM_LOSHIFT		29
8672fd7236SJulien Thierry #define ADR_IMM_HISHIFT		5
8772fd7236SJulien Thierry 
aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type,u32 insn)8872fd7236SJulien Thierry u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn)
8972fd7236SJulien Thierry {
9072fd7236SJulien Thierry 	u32 immlo, immhi, mask;
9172fd7236SJulien Thierry 	int shift;
9272fd7236SJulien Thierry 
9372fd7236SJulien Thierry 	switch (type) {
9472fd7236SJulien Thierry 	case AARCH64_INSN_IMM_ADR:
9572fd7236SJulien Thierry 		shift = 0;
9672fd7236SJulien Thierry 		immlo = (insn >> ADR_IMM_LOSHIFT) & ADR_IMM_LOMASK;
9772fd7236SJulien Thierry 		immhi = (insn >> ADR_IMM_HISHIFT) & ADR_IMM_HIMASK;
9872fd7236SJulien Thierry 		insn = (immhi << ADR_IMM_HILOSPLIT) | immlo;
9972fd7236SJulien Thierry 		mask = ADR_IMM_SIZE - 1;
10072fd7236SJulien Thierry 		break;
10172fd7236SJulien Thierry 	default:
10272fd7236SJulien Thierry 		if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
1032806556cSJason Wang 			pr_err("%s: unknown immediate encoding %d\n", __func__,
10472fd7236SJulien Thierry 			       type);
10572fd7236SJulien Thierry 			return 0;
10672fd7236SJulien Thierry 		}
10772fd7236SJulien Thierry 	}
10872fd7236SJulien Thierry 
10972fd7236SJulien Thierry 	return (insn >> shift) & mask;
11072fd7236SJulien Thierry }
11172fd7236SJulien Thierry 
aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,u32 insn,u64 imm)11272fd7236SJulien Thierry u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
11372fd7236SJulien Thierry 				  u32 insn, u64 imm)
11472fd7236SJulien Thierry {
11572fd7236SJulien Thierry 	u32 immlo, immhi, mask;
11672fd7236SJulien Thierry 	int shift;
11772fd7236SJulien Thierry 
11872fd7236SJulien Thierry 	if (insn == AARCH64_BREAK_FAULT)
11972fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
12072fd7236SJulien Thierry 
12172fd7236SJulien Thierry 	switch (type) {
12272fd7236SJulien Thierry 	case AARCH64_INSN_IMM_ADR:
12372fd7236SJulien Thierry 		shift = 0;
12472fd7236SJulien Thierry 		immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT;
12572fd7236SJulien Thierry 		imm >>= ADR_IMM_HILOSPLIT;
12672fd7236SJulien Thierry 		immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT;
12772fd7236SJulien Thierry 		imm = immlo | immhi;
12872fd7236SJulien Thierry 		mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) |
12972fd7236SJulien Thierry 			(ADR_IMM_HIMASK << ADR_IMM_HISHIFT));
13072fd7236SJulien Thierry 		break;
13172fd7236SJulien Thierry 	default:
13272fd7236SJulien Thierry 		if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
1332806556cSJason Wang 			pr_err("%s: unknown immediate encoding %d\n", __func__,
13472fd7236SJulien Thierry 			       type);
13572fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
13672fd7236SJulien Thierry 		}
13772fd7236SJulien Thierry 	}
13872fd7236SJulien Thierry 
13972fd7236SJulien Thierry 	/* Update the immediate field. */
14072fd7236SJulien Thierry 	insn &= ~(mask << shift);
14172fd7236SJulien Thierry 	insn |= (imm & mask) << shift;
14272fd7236SJulien Thierry 
14372fd7236SJulien Thierry 	return insn;
14472fd7236SJulien Thierry }
14572fd7236SJulien Thierry 
aarch64_insn_decode_register(enum aarch64_insn_register_type type,u32 insn)14672fd7236SJulien Thierry u32 aarch64_insn_decode_register(enum aarch64_insn_register_type type,
14772fd7236SJulien Thierry 					u32 insn)
14872fd7236SJulien Thierry {
14972fd7236SJulien Thierry 	int shift;
15072fd7236SJulien Thierry 
15172fd7236SJulien Thierry 	switch (type) {
15272fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RT:
15372fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RD:
15472fd7236SJulien Thierry 		shift = 0;
15572fd7236SJulien Thierry 		break;
15672fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RN:
15772fd7236SJulien Thierry 		shift = 5;
15872fd7236SJulien Thierry 		break;
15972fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RT2:
16072fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RA:
16172fd7236SJulien Thierry 		shift = 10;
16272fd7236SJulien Thierry 		break;
16372fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RM:
16472fd7236SJulien Thierry 		shift = 16;
16572fd7236SJulien Thierry 		break;
16672fd7236SJulien Thierry 	default:
16772fd7236SJulien Thierry 		pr_err("%s: unknown register type encoding %d\n", __func__,
16872fd7236SJulien Thierry 		       type);
16972fd7236SJulien Thierry 		return 0;
17072fd7236SJulien Thierry 	}
17172fd7236SJulien Thierry 
17272fd7236SJulien Thierry 	return (insn >> shift) & GENMASK(4, 0);
17372fd7236SJulien Thierry }
17472fd7236SJulien Thierry 
aarch64_insn_encode_register(enum aarch64_insn_register_type type,u32 insn,enum aarch64_insn_register reg)17572fd7236SJulien Thierry static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type,
17672fd7236SJulien Thierry 					u32 insn,
17772fd7236SJulien Thierry 					enum aarch64_insn_register reg)
17872fd7236SJulien Thierry {
17972fd7236SJulien Thierry 	int shift;
18072fd7236SJulien Thierry 
18172fd7236SJulien Thierry 	if (insn == AARCH64_BREAK_FAULT)
18272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
18372fd7236SJulien Thierry 
18472fd7236SJulien Thierry 	if (reg < AARCH64_INSN_REG_0 || reg > AARCH64_INSN_REG_SP) {
18572fd7236SJulien Thierry 		pr_err("%s: unknown register encoding %d\n", __func__, reg);
18672fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
18772fd7236SJulien Thierry 	}
18872fd7236SJulien Thierry 
18972fd7236SJulien Thierry 	switch (type) {
19072fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RT:
19172fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RD:
19272fd7236SJulien Thierry 		shift = 0;
19372fd7236SJulien Thierry 		break;
19472fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RN:
19572fd7236SJulien Thierry 		shift = 5;
19672fd7236SJulien Thierry 		break;
19772fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RT2:
19872fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RA:
19972fd7236SJulien Thierry 		shift = 10;
20072fd7236SJulien Thierry 		break;
20172fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RM:
20272fd7236SJulien Thierry 	case AARCH64_INSN_REGTYPE_RS:
20372fd7236SJulien Thierry 		shift = 16;
20472fd7236SJulien Thierry 		break;
20572fd7236SJulien Thierry 	default:
20672fd7236SJulien Thierry 		pr_err("%s: unknown register type encoding %d\n", __func__,
20772fd7236SJulien Thierry 		       type);
20872fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
20972fd7236SJulien Thierry 	}
21072fd7236SJulien Thierry 
21172fd7236SJulien Thierry 	insn &= ~(GENMASK(4, 0) << shift);
21272fd7236SJulien Thierry 	insn |= reg << shift;
21372fd7236SJulien Thierry 
21472fd7236SJulien Thierry 	return insn;
21572fd7236SJulien Thierry }
21672fd7236SJulien Thierry 
21730c90f67SXu Kuohai static const u32 aarch64_insn_ldst_size[] = {
21830c90f67SXu Kuohai 	[AARCH64_INSN_SIZE_8] = 0,
21930c90f67SXu Kuohai 	[AARCH64_INSN_SIZE_16] = 1,
22030c90f67SXu Kuohai 	[AARCH64_INSN_SIZE_32] = 2,
22130c90f67SXu Kuohai 	[AARCH64_INSN_SIZE_64] = 3,
22230c90f67SXu Kuohai };
22330c90f67SXu Kuohai 
aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type,u32 insn)22472fd7236SJulien Thierry static u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type,
22572fd7236SJulien Thierry 					 u32 insn)
22672fd7236SJulien Thierry {
22772fd7236SJulien Thierry 	u32 size;
22872fd7236SJulien Thierry 
22930c90f67SXu Kuohai 	if (type < AARCH64_INSN_SIZE_8 || type > AARCH64_INSN_SIZE_64) {
23072fd7236SJulien Thierry 		pr_err("%s: unknown size encoding %d\n", __func__, type);
23172fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
23272fd7236SJulien Thierry 	}
23372fd7236SJulien Thierry 
23430c90f67SXu Kuohai 	size = aarch64_insn_ldst_size[type];
23572fd7236SJulien Thierry 	insn &= ~GENMASK(31, 30);
23672fd7236SJulien Thierry 	insn |= size << 30;
23772fd7236SJulien Thierry 
23872fd7236SJulien Thierry 	return insn;
23972fd7236SJulien Thierry }
24072fd7236SJulien Thierry 
label_imm_common(unsigned long pc,unsigned long addr,long range)241f1e8a24eSXu Kuohai static inline long label_imm_common(unsigned long pc, unsigned long addr,
24272fd7236SJulien Thierry 				     long range)
24372fd7236SJulien Thierry {
24472fd7236SJulien Thierry 	long offset;
24572fd7236SJulien Thierry 
24672fd7236SJulien Thierry 	if ((pc & 0x3) || (addr & 0x3)) {
24772fd7236SJulien Thierry 		pr_err("%s: A64 instructions must be word aligned\n", __func__);
24872fd7236SJulien Thierry 		return range;
24972fd7236SJulien Thierry 	}
25072fd7236SJulien Thierry 
25172fd7236SJulien Thierry 	offset = ((long)addr - (long)pc);
25272fd7236SJulien Thierry 
25372fd7236SJulien Thierry 	if (offset < -range || offset >= range) {
25472fd7236SJulien Thierry 		pr_err("%s: offset out of range\n", __func__);
25572fd7236SJulien Thierry 		return range;
25672fd7236SJulien Thierry 	}
25772fd7236SJulien Thierry 
25872fd7236SJulien Thierry 	return offset;
25972fd7236SJulien Thierry }
26072fd7236SJulien Thierry 
aarch64_insn_gen_branch_imm(unsigned long pc,unsigned long addr,enum aarch64_insn_branch_type type)26172fd7236SJulien Thierry u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
26272fd7236SJulien Thierry 					  enum aarch64_insn_branch_type type)
26372fd7236SJulien Thierry {
26472fd7236SJulien Thierry 	u32 insn;
26572fd7236SJulien Thierry 	long offset;
26672fd7236SJulien Thierry 
26772fd7236SJulien Thierry 	/*
26872fd7236SJulien Thierry 	 * B/BL support [-128M, 128M) offset
26972fd7236SJulien Thierry 	 * ARM64 virtual address arrangement guarantees all kernel and module
27072fd7236SJulien Thierry 	 * texts are within +/-128M.
27172fd7236SJulien Thierry 	 */
272f1e8a24eSXu Kuohai 	offset = label_imm_common(pc, addr, SZ_128M);
27372fd7236SJulien Thierry 	if (offset >= SZ_128M)
27472fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
27572fd7236SJulien Thierry 
27672fd7236SJulien Thierry 	switch (type) {
27772fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_LINK:
27872fd7236SJulien Thierry 		insn = aarch64_insn_get_bl_value();
27972fd7236SJulien Thierry 		break;
28072fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_NOLINK:
28172fd7236SJulien Thierry 		insn = aarch64_insn_get_b_value();
28272fd7236SJulien Thierry 		break;
28372fd7236SJulien Thierry 	default:
28472fd7236SJulien Thierry 		pr_err("%s: unknown branch encoding %d\n", __func__, type);
28572fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
28672fd7236SJulien Thierry 	}
28772fd7236SJulien Thierry 
28872fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
28972fd7236SJulien Thierry 					     offset >> 2);
29072fd7236SJulien Thierry }
29172fd7236SJulien Thierry 
aarch64_insn_gen_comp_branch_imm(unsigned long pc,unsigned long addr,enum aarch64_insn_register reg,enum aarch64_insn_variant variant,enum aarch64_insn_branch_type type)29272fd7236SJulien Thierry u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
29372fd7236SJulien Thierry 				     enum aarch64_insn_register reg,
29472fd7236SJulien Thierry 				     enum aarch64_insn_variant variant,
29572fd7236SJulien Thierry 				     enum aarch64_insn_branch_type type)
29672fd7236SJulien Thierry {
29772fd7236SJulien Thierry 	u32 insn;
29872fd7236SJulien Thierry 	long offset;
29972fd7236SJulien Thierry 
300f1e8a24eSXu Kuohai 	offset = label_imm_common(pc, addr, SZ_1M);
30172fd7236SJulien Thierry 	if (offset >= SZ_1M)
30272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
30372fd7236SJulien Thierry 
30472fd7236SJulien Thierry 	switch (type) {
30572fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_COMP_ZERO:
30672fd7236SJulien Thierry 		insn = aarch64_insn_get_cbz_value();
30772fd7236SJulien Thierry 		break;
30872fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_COMP_NONZERO:
30972fd7236SJulien Thierry 		insn = aarch64_insn_get_cbnz_value();
31072fd7236SJulien Thierry 		break;
31172fd7236SJulien Thierry 	default:
31272fd7236SJulien Thierry 		pr_err("%s: unknown branch encoding %d\n", __func__, type);
31372fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
31472fd7236SJulien Thierry 	}
31572fd7236SJulien Thierry 
31672fd7236SJulien Thierry 	switch (variant) {
31772fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
31872fd7236SJulien Thierry 		break;
31972fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
32072fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
32172fd7236SJulien Thierry 		break;
32272fd7236SJulien Thierry 	default:
32372fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
32472fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
32572fd7236SJulien Thierry 	}
32672fd7236SJulien Thierry 
32772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
32872fd7236SJulien Thierry 
32972fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
33072fd7236SJulien Thierry 					     offset >> 2);
33172fd7236SJulien Thierry }
33272fd7236SJulien Thierry 
aarch64_insn_gen_cond_branch_imm(unsigned long pc,unsigned long addr,enum aarch64_insn_condition cond)33372fd7236SJulien Thierry u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
33472fd7236SJulien Thierry 				     enum aarch64_insn_condition cond)
33572fd7236SJulien Thierry {
33672fd7236SJulien Thierry 	u32 insn;
33772fd7236SJulien Thierry 	long offset;
33872fd7236SJulien Thierry 
339f1e8a24eSXu Kuohai 	offset = label_imm_common(pc, addr, SZ_1M);
34072fd7236SJulien Thierry 
34172fd7236SJulien Thierry 	insn = aarch64_insn_get_bcond_value();
34272fd7236SJulien Thierry 
34372fd7236SJulien Thierry 	if (cond < AARCH64_INSN_COND_EQ || cond > AARCH64_INSN_COND_AL) {
34472fd7236SJulien Thierry 		pr_err("%s: unknown condition encoding %d\n", __func__, cond);
34572fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
34672fd7236SJulien Thierry 	}
34772fd7236SJulien Thierry 	insn |= cond;
34872fd7236SJulien Thierry 
34972fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
35072fd7236SJulien Thierry 					     offset >> 2);
35172fd7236SJulien Thierry }
35272fd7236SJulien Thierry 
aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg,enum aarch64_insn_branch_type type)35372fd7236SJulien Thierry u32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg,
35472fd7236SJulien Thierry 				enum aarch64_insn_branch_type type)
35572fd7236SJulien Thierry {
35672fd7236SJulien Thierry 	u32 insn;
35772fd7236SJulien Thierry 
35872fd7236SJulien Thierry 	switch (type) {
35972fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_NOLINK:
36072fd7236SJulien Thierry 		insn = aarch64_insn_get_br_value();
36172fd7236SJulien Thierry 		break;
36272fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_LINK:
36372fd7236SJulien Thierry 		insn = aarch64_insn_get_blr_value();
36472fd7236SJulien Thierry 		break;
36572fd7236SJulien Thierry 	case AARCH64_INSN_BRANCH_RETURN:
36672fd7236SJulien Thierry 		insn = aarch64_insn_get_ret_value();
36772fd7236SJulien Thierry 		break;
36872fd7236SJulien Thierry 	default:
36972fd7236SJulien Thierry 		pr_err("%s: unknown branch encoding %d\n", __func__, type);
37072fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
37172fd7236SJulien Thierry 	}
37272fd7236SJulien Thierry 
37372fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, reg);
37472fd7236SJulien Thierry }
37572fd7236SJulien Thierry 
aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg,enum aarch64_insn_register base,enum aarch64_insn_register offset,enum aarch64_insn_size_type size,enum aarch64_insn_ldst_type type)37672fd7236SJulien Thierry u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg,
37772fd7236SJulien Thierry 				    enum aarch64_insn_register base,
37872fd7236SJulien Thierry 				    enum aarch64_insn_register offset,
37972fd7236SJulien Thierry 				    enum aarch64_insn_size_type size,
38072fd7236SJulien Thierry 				    enum aarch64_insn_ldst_type type)
38172fd7236SJulien Thierry {
38272fd7236SJulien Thierry 	u32 insn;
38372fd7236SJulien Thierry 
38472fd7236SJulien Thierry 	switch (type) {
38572fd7236SJulien Thierry 	case AARCH64_INSN_LDST_LOAD_REG_OFFSET:
38672fd7236SJulien Thierry 		insn = aarch64_insn_get_ldr_reg_value();
38772fd7236SJulien Thierry 		break;
388*6c9f86d3SXu Kuohai 	case AARCH64_INSN_LDST_SIGNED_LOAD_REG_OFFSET:
389*6c9f86d3SXu Kuohai 		insn = aarch64_insn_get_signed_ldr_reg_value();
390*6c9f86d3SXu Kuohai 		break;
39172fd7236SJulien Thierry 	case AARCH64_INSN_LDST_STORE_REG_OFFSET:
39272fd7236SJulien Thierry 		insn = aarch64_insn_get_str_reg_value();
39372fd7236SJulien Thierry 		break;
39472fd7236SJulien Thierry 	default:
39572fd7236SJulien Thierry 		pr_err("%s: unknown load/store encoding %d\n", __func__, type);
39672fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
39772fd7236SJulien Thierry 	}
39872fd7236SJulien Thierry 
39972fd7236SJulien Thierry 	insn = aarch64_insn_encode_ldst_size(size, insn);
40072fd7236SJulien Thierry 
40172fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
40272fd7236SJulien Thierry 
40372fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
40472fd7236SJulien Thierry 					    base);
40572fd7236SJulien Thierry 
40672fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn,
40772fd7236SJulien Thierry 					    offset);
40872fd7236SJulien Thierry }
40972fd7236SJulien Thierry 
aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,enum aarch64_insn_register base,unsigned int imm,enum aarch64_insn_size_type size,enum aarch64_insn_ldst_type type)41030c90f67SXu Kuohai u32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,
41130c90f67SXu Kuohai 				    enum aarch64_insn_register base,
41230c90f67SXu Kuohai 				    unsigned int imm,
41330c90f67SXu Kuohai 				    enum aarch64_insn_size_type size,
41430c90f67SXu Kuohai 				    enum aarch64_insn_ldst_type type)
41530c90f67SXu Kuohai {
41630c90f67SXu Kuohai 	u32 insn;
41730c90f67SXu Kuohai 	u32 shift;
41830c90f67SXu Kuohai 
41930c90f67SXu Kuohai 	if (size < AARCH64_INSN_SIZE_8 || size > AARCH64_INSN_SIZE_64) {
42030c90f67SXu Kuohai 		pr_err("%s: unknown size encoding %d\n", __func__, type);
42130c90f67SXu Kuohai 		return AARCH64_BREAK_FAULT;
42230c90f67SXu Kuohai 	}
42330c90f67SXu Kuohai 
42430c90f67SXu Kuohai 	shift = aarch64_insn_ldst_size[size];
42530c90f67SXu Kuohai 	if (imm & ~(BIT(12 + shift) - BIT(shift))) {
42630c90f67SXu Kuohai 		pr_err("%s: invalid imm: %d\n", __func__, imm);
42730c90f67SXu Kuohai 		return AARCH64_BREAK_FAULT;
42830c90f67SXu Kuohai 	}
42930c90f67SXu Kuohai 
43030c90f67SXu Kuohai 	imm >>= shift;
43130c90f67SXu Kuohai 
43230c90f67SXu Kuohai 	switch (type) {
43330c90f67SXu Kuohai 	case AARCH64_INSN_LDST_LOAD_IMM_OFFSET:
43430c90f67SXu Kuohai 		insn = aarch64_insn_get_ldr_imm_value();
43530c90f67SXu Kuohai 		break;
436*6c9f86d3SXu Kuohai 	case AARCH64_INSN_LDST_SIGNED_LOAD_IMM_OFFSET:
437*6c9f86d3SXu Kuohai 		insn = aarch64_insn_get_signed_load_imm_value();
438*6c9f86d3SXu Kuohai 		break;
43930c90f67SXu Kuohai 	case AARCH64_INSN_LDST_STORE_IMM_OFFSET:
44030c90f67SXu Kuohai 		insn = aarch64_insn_get_str_imm_value();
44130c90f67SXu Kuohai 		break;
44230c90f67SXu Kuohai 	default:
44330c90f67SXu Kuohai 		pr_err("%s: unknown load/store encoding %d\n", __func__, type);
44430c90f67SXu Kuohai 		return AARCH64_BREAK_FAULT;
44530c90f67SXu Kuohai 	}
44630c90f67SXu Kuohai 
44730c90f67SXu Kuohai 	insn = aarch64_insn_encode_ldst_size(size, insn);
44830c90f67SXu Kuohai 
44930c90f67SXu Kuohai 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
45030c90f67SXu Kuohai 
45130c90f67SXu Kuohai 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
45230c90f67SXu Kuohai 					    base);
45330c90f67SXu Kuohai 
45430c90f67SXu Kuohai 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm);
45530c90f67SXu Kuohai }
45630c90f67SXu Kuohai 
aarch64_insn_gen_load_literal(unsigned long pc,unsigned long addr,enum aarch64_insn_register reg,bool is64bit)457f1e8a24eSXu Kuohai u32 aarch64_insn_gen_load_literal(unsigned long pc, unsigned long addr,
458f1e8a24eSXu Kuohai 				  enum aarch64_insn_register reg,
459f1e8a24eSXu Kuohai 				  bool is64bit)
460f1e8a24eSXu Kuohai {
461f1e8a24eSXu Kuohai 	u32 insn;
462f1e8a24eSXu Kuohai 	long offset;
463f1e8a24eSXu Kuohai 
464f1e8a24eSXu Kuohai 	offset = label_imm_common(pc, addr, SZ_1M);
465f1e8a24eSXu Kuohai 	if (offset >= SZ_1M)
466f1e8a24eSXu Kuohai 		return AARCH64_BREAK_FAULT;
467f1e8a24eSXu Kuohai 
468f1e8a24eSXu Kuohai 	insn = aarch64_insn_get_ldr_lit_value();
469f1e8a24eSXu Kuohai 
470f1e8a24eSXu Kuohai 	if (is64bit)
471f1e8a24eSXu Kuohai 		insn |= BIT(30);
472f1e8a24eSXu Kuohai 
473f1e8a24eSXu Kuohai 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
474f1e8a24eSXu Kuohai 
475f1e8a24eSXu Kuohai 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
476f1e8a24eSXu Kuohai 					     offset >> 2);
477f1e8a24eSXu Kuohai }
478f1e8a24eSXu Kuohai 
aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,enum aarch64_insn_register reg2,enum aarch64_insn_register base,int offset,enum aarch64_insn_variant variant,enum aarch64_insn_ldst_type type)47972fd7236SJulien Thierry u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,
48072fd7236SJulien Thierry 				     enum aarch64_insn_register reg2,
48172fd7236SJulien Thierry 				     enum aarch64_insn_register base,
48272fd7236SJulien Thierry 				     int offset,
48372fd7236SJulien Thierry 				     enum aarch64_insn_variant variant,
48472fd7236SJulien Thierry 				     enum aarch64_insn_ldst_type type)
48572fd7236SJulien Thierry {
48672fd7236SJulien Thierry 	u32 insn;
48772fd7236SJulien Thierry 	int shift;
48872fd7236SJulien Thierry 
48972fd7236SJulien Thierry 	switch (type) {
49072fd7236SJulien Thierry 	case AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX:
49172fd7236SJulien Thierry 		insn = aarch64_insn_get_ldp_pre_value();
49272fd7236SJulien Thierry 		break;
49372fd7236SJulien Thierry 	case AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX:
49472fd7236SJulien Thierry 		insn = aarch64_insn_get_stp_pre_value();
49572fd7236SJulien Thierry 		break;
49672fd7236SJulien Thierry 	case AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX:
49772fd7236SJulien Thierry 		insn = aarch64_insn_get_ldp_post_value();
49872fd7236SJulien Thierry 		break;
49972fd7236SJulien Thierry 	case AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX:
50072fd7236SJulien Thierry 		insn = aarch64_insn_get_stp_post_value();
50172fd7236SJulien Thierry 		break;
50272fd7236SJulien Thierry 	default:
50372fd7236SJulien Thierry 		pr_err("%s: unknown load/store encoding %d\n", __func__, type);
50472fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
50572fd7236SJulien Thierry 	}
50672fd7236SJulien Thierry 
50772fd7236SJulien Thierry 	switch (variant) {
50872fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
50972fd7236SJulien Thierry 		if ((offset & 0x3) || (offset < -256) || (offset > 252)) {
51072fd7236SJulien Thierry 			pr_err("%s: offset must be multiples of 4 in the range of [-256, 252] %d\n",
51172fd7236SJulien Thierry 			       __func__, offset);
51272fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
51372fd7236SJulien Thierry 		}
51472fd7236SJulien Thierry 		shift = 2;
51572fd7236SJulien Thierry 		break;
51672fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
51772fd7236SJulien Thierry 		if ((offset & 0x7) || (offset < -512) || (offset > 504)) {
51872fd7236SJulien Thierry 			pr_err("%s: offset must be multiples of 8 in the range of [-512, 504] %d\n",
51972fd7236SJulien Thierry 			       __func__, offset);
52072fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
52172fd7236SJulien Thierry 		}
52272fd7236SJulien Thierry 		shift = 3;
52372fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
52472fd7236SJulien Thierry 		break;
52572fd7236SJulien Thierry 	default:
52672fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
52772fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
52872fd7236SJulien Thierry 	}
52972fd7236SJulien Thierry 
53072fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn,
53172fd7236SJulien Thierry 					    reg1);
53272fd7236SJulien Thierry 
53372fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn,
53472fd7236SJulien Thierry 					    reg2);
53572fd7236SJulien Thierry 
53672fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
53772fd7236SJulien Thierry 					    base);
53872fd7236SJulien Thierry 
53972fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_7, insn,
54072fd7236SJulien Thierry 					     offset >> shift);
54172fd7236SJulien Thierry }
54272fd7236SJulien Thierry 
aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg,enum aarch64_insn_register base,enum aarch64_insn_register state,enum aarch64_insn_size_type size,enum aarch64_insn_ldst_type type)54372fd7236SJulien Thierry u32 aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg,
54472fd7236SJulien Thierry 				   enum aarch64_insn_register base,
54572fd7236SJulien Thierry 				   enum aarch64_insn_register state,
54672fd7236SJulien Thierry 				   enum aarch64_insn_size_type size,
54772fd7236SJulien Thierry 				   enum aarch64_insn_ldst_type type)
54872fd7236SJulien Thierry {
54972fd7236SJulien Thierry 	u32 insn;
55072fd7236SJulien Thierry 
55172fd7236SJulien Thierry 	switch (type) {
55272fd7236SJulien Thierry 	case AARCH64_INSN_LDST_LOAD_EX:
553fa1114d9SHou Tao 	case AARCH64_INSN_LDST_LOAD_ACQ_EX:
55472fd7236SJulien Thierry 		insn = aarch64_insn_get_load_ex_value();
555fa1114d9SHou Tao 		if (type == AARCH64_INSN_LDST_LOAD_ACQ_EX)
556fa1114d9SHou Tao 			insn |= BIT(15);
55772fd7236SJulien Thierry 		break;
55872fd7236SJulien Thierry 	case AARCH64_INSN_LDST_STORE_EX:
559fa1114d9SHou Tao 	case AARCH64_INSN_LDST_STORE_REL_EX:
56072fd7236SJulien Thierry 		insn = aarch64_insn_get_store_ex_value();
561fa1114d9SHou Tao 		if (type == AARCH64_INSN_LDST_STORE_REL_EX)
562fa1114d9SHou Tao 			insn |= BIT(15);
56372fd7236SJulien Thierry 		break;
56472fd7236SJulien Thierry 	default:
56572fd7236SJulien Thierry 		pr_err("%s: unknown load/store exclusive encoding %d\n", __func__, type);
56672fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
56772fd7236SJulien Thierry 	}
56872fd7236SJulien Thierry 
56972fd7236SJulien Thierry 	insn = aarch64_insn_encode_ldst_size(size, insn);
57072fd7236SJulien Thierry 
57172fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn,
57272fd7236SJulien Thierry 					    reg);
57372fd7236SJulien Thierry 
57472fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
57572fd7236SJulien Thierry 					    base);
57672fd7236SJulien Thierry 
57772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn,
57872fd7236SJulien Thierry 					    AARCH64_INSN_REG_ZR);
57972fd7236SJulien Thierry 
58072fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn,
58172fd7236SJulien Thierry 					    state);
58272fd7236SJulien Thierry }
58372fd7236SJulien Thierry 
584fa1114d9SHou Tao #ifdef CONFIG_ARM64_LSE_ATOMICS
aarch64_insn_encode_ldst_order(enum aarch64_insn_mem_order_type type,u32 insn)585fa1114d9SHou Tao static u32 aarch64_insn_encode_ldst_order(enum aarch64_insn_mem_order_type type,
586fa1114d9SHou Tao 					  u32 insn)
587fa1114d9SHou Tao {
588fa1114d9SHou Tao 	u32 order;
589fa1114d9SHou Tao 
590fa1114d9SHou Tao 	switch (type) {
591fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_NONE:
592fa1114d9SHou Tao 		order = 0;
593fa1114d9SHou Tao 		break;
594fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_ACQ:
595fa1114d9SHou Tao 		order = 2;
596fa1114d9SHou Tao 		break;
597fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_REL:
598fa1114d9SHou Tao 		order = 1;
599fa1114d9SHou Tao 		break;
600fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_ACQREL:
601fa1114d9SHou Tao 		order = 3;
602fa1114d9SHou Tao 		break;
603fa1114d9SHou Tao 	default:
604fa1114d9SHou Tao 		pr_err("%s: unknown mem order %d\n", __func__, type);
605fa1114d9SHou Tao 		return AARCH64_BREAK_FAULT;
606fa1114d9SHou Tao 	}
607fa1114d9SHou Tao 
608fa1114d9SHou Tao 	insn &= ~GENMASK(23, 22);
609fa1114d9SHou Tao 	insn |= order << 22;
610fa1114d9SHou Tao 
611fa1114d9SHou Tao 	return insn;
612fa1114d9SHou Tao }
613fa1114d9SHou Tao 
aarch64_insn_gen_atomic_ld_op(enum aarch64_insn_register result,enum aarch64_insn_register address,enum aarch64_insn_register value,enum aarch64_insn_size_type size,enum aarch64_insn_mem_atomic_op op,enum aarch64_insn_mem_order_type order)614fa1114d9SHou Tao u32 aarch64_insn_gen_atomic_ld_op(enum aarch64_insn_register result,
61572fd7236SJulien Thierry 				  enum aarch64_insn_register address,
61672fd7236SJulien Thierry 				  enum aarch64_insn_register value,
617fa1114d9SHou Tao 				  enum aarch64_insn_size_type size,
618fa1114d9SHou Tao 				  enum aarch64_insn_mem_atomic_op op,
619fa1114d9SHou Tao 				  enum aarch64_insn_mem_order_type order)
62072fd7236SJulien Thierry {
621fa1114d9SHou Tao 	u32 insn;
622fa1114d9SHou Tao 
623fa1114d9SHou Tao 	switch (op) {
624fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ATOMIC_ADD:
625fa1114d9SHou Tao 		insn = aarch64_insn_get_ldadd_value();
626fa1114d9SHou Tao 		break;
627fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ATOMIC_CLR:
628fa1114d9SHou Tao 		insn = aarch64_insn_get_ldclr_value();
629fa1114d9SHou Tao 		break;
630fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ATOMIC_EOR:
631fa1114d9SHou Tao 		insn = aarch64_insn_get_ldeor_value();
632fa1114d9SHou Tao 		break;
633fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ATOMIC_SET:
634fa1114d9SHou Tao 		insn = aarch64_insn_get_ldset_value();
635fa1114d9SHou Tao 		break;
636fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ATOMIC_SWP:
637fa1114d9SHou Tao 		insn = aarch64_insn_get_swp_value();
638fa1114d9SHou Tao 		break;
639fa1114d9SHou Tao 	default:
640fa1114d9SHou Tao 		pr_err("%s: unimplemented mem atomic op %d\n", __func__, op);
641fa1114d9SHou Tao 		return AARCH64_BREAK_FAULT;
642fa1114d9SHou Tao 	}
64372fd7236SJulien Thierry 
64472fd7236SJulien Thierry 	switch (size) {
64572fd7236SJulien Thierry 	case AARCH64_INSN_SIZE_32:
64672fd7236SJulien Thierry 	case AARCH64_INSN_SIZE_64:
64772fd7236SJulien Thierry 		break;
64872fd7236SJulien Thierry 	default:
64972fd7236SJulien Thierry 		pr_err("%s: unimplemented size encoding %d\n", __func__, size);
65072fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
65172fd7236SJulien Thierry 	}
65272fd7236SJulien Thierry 
65372fd7236SJulien Thierry 	insn = aarch64_insn_encode_ldst_size(size, insn);
65472fd7236SJulien Thierry 
655fa1114d9SHou Tao 	insn = aarch64_insn_encode_ldst_order(order, insn);
656fa1114d9SHou Tao 
65772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn,
65872fd7236SJulien Thierry 					    result);
65972fd7236SJulien Thierry 
66072fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
66172fd7236SJulien Thierry 					    address);
66272fd7236SJulien Thierry 
66372fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn,
66472fd7236SJulien Thierry 					    value);
66572fd7236SJulien Thierry }
66672fd7236SJulien Thierry 
aarch64_insn_encode_cas_order(enum aarch64_insn_mem_order_type type,u32 insn)667fa1114d9SHou Tao static u32 aarch64_insn_encode_cas_order(enum aarch64_insn_mem_order_type type,
668fa1114d9SHou Tao 					 u32 insn)
66972fd7236SJulien Thierry {
670fa1114d9SHou Tao 	u32 order;
671fa1114d9SHou Tao 
672fa1114d9SHou Tao 	switch (type) {
673fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_NONE:
674fa1114d9SHou Tao 		order = 0;
675fa1114d9SHou Tao 		break;
676fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_ACQ:
677fa1114d9SHou Tao 		order = BIT(22);
678fa1114d9SHou Tao 		break;
679fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_REL:
680fa1114d9SHou Tao 		order = BIT(15);
681fa1114d9SHou Tao 		break;
682fa1114d9SHou Tao 	case AARCH64_INSN_MEM_ORDER_ACQREL:
683fa1114d9SHou Tao 		order = BIT(15) | BIT(22);
684fa1114d9SHou Tao 		break;
685fa1114d9SHou Tao 	default:
686fa1114d9SHou Tao 		pr_err("%s: unknown mem order %d\n", __func__, type);
687fa1114d9SHou Tao 		return AARCH64_BREAK_FAULT;
68872fd7236SJulien Thierry 	}
68972fd7236SJulien Thierry 
690fa1114d9SHou Tao 	insn &= ~(BIT(15) | BIT(22));
691fa1114d9SHou Tao 	insn |= order;
692fa1114d9SHou Tao 
693fa1114d9SHou Tao 	return insn;
694fa1114d9SHou Tao }
695fa1114d9SHou Tao 
aarch64_insn_gen_cas(enum aarch64_insn_register result,enum aarch64_insn_register address,enum aarch64_insn_register value,enum aarch64_insn_size_type size,enum aarch64_insn_mem_order_type order)696fa1114d9SHou Tao u32 aarch64_insn_gen_cas(enum aarch64_insn_register result,
697fa1114d9SHou Tao 			 enum aarch64_insn_register address,
698fa1114d9SHou Tao 			 enum aarch64_insn_register value,
699fa1114d9SHou Tao 			 enum aarch64_insn_size_type size,
700fa1114d9SHou Tao 			 enum aarch64_insn_mem_order_type order)
701fa1114d9SHou Tao {
702fa1114d9SHou Tao 	u32 insn;
703fa1114d9SHou Tao 
704fa1114d9SHou Tao 	switch (size) {
705fa1114d9SHou Tao 	case AARCH64_INSN_SIZE_32:
706fa1114d9SHou Tao 	case AARCH64_INSN_SIZE_64:
707fa1114d9SHou Tao 		break;
708fa1114d9SHou Tao 	default:
709fa1114d9SHou Tao 		pr_err("%s: unimplemented size encoding %d\n", __func__, size);
710fa1114d9SHou Tao 		return AARCH64_BREAK_FAULT;
711fa1114d9SHou Tao 	}
712fa1114d9SHou Tao 
713fa1114d9SHou Tao 	insn = aarch64_insn_get_cas_value();
714fa1114d9SHou Tao 
715fa1114d9SHou Tao 	insn = aarch64_insn_encode_ldst_size(size, insn);
716fa1114d9SHou Tao 
717fa1114d9SHou Tao 	insn = aarch64_insn_encode_cas_order(order, insn);
718fa1114d9SHou Tao 
719fa1114d9SHou Tao 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn,
720fa1114d9SHou Tao 					    result);
721fa1114d9SHou Tao 
722fa1114d9SHou Tao 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
723fa1114d9SHou Tao 					    address);
724fa1114d9SHou Tao 
725fa1114d9SHou Tao 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn,
726fa1114d9SHou Tao 					    value);
727fa1114d9SHou Tao }
728fa1114d9SHou Tao #endif
729fa1114d9SHou Tao 
aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst,enum aarch64_insn_register src,int imm,enum aarch64_insn_variant variant,enum aarch64_insn_adsb_type type)73072fd7236SJulien Thierry u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst,
73172fd7236SJulien Thierry 				 enum aarch64_insn_register src,
73272fd7236SJulien Thierry 				 int imm, enum aarch64_insn_variant variant,
73372fd7236SJulien Thierry 				 enum aarch64_insn_adsb_type type)
73472fd7236SJulien Thierry {
73572fd7236SJulien Thierry 	u32 insn;
73672fd7236SJulien Thierry 
73772fd7236SJulien Thierry 	switch (type) {
73872fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_ADD:
73972fd7236SJulien Thierry 		insn = aarch64_insn_get_add_imm_value();
74072fd7236SJulien Thierry 		break;
74172fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_SUB:
74272fd7236SJulien Thierry 		insn = aarch64_insn_get_sub_imm_value();
74372fd7236SJulien Thierry 		break;
74472fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_ADD_SETFLAGS:
74572fd7236SJulien Thierry 		insn = aarch64_insn_get_adds_imm_value();
74672fd7236SJulien Thierry 		break;
74772fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_SUB_SETFLAGS:
74872fd7236SJulien Thierry 		insn = aarch64_insn_get_subs_imm_value();
74972fd7236SJulien Thierry 		break;
75072fd7236SJulien Thierry 	default:
75172fd7236SJulien Thierry 		pr_err("%s: unknown add/sub encoding %d\n", __func__, type);
75272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
75372fd7236SJulien Thierry 	}
75472fd7236SJulien Thierry 
75572fd7236SJulien Thierry 	switch (variant) {
75672fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
75772fd7236SJulien Thierry 		break;
75872fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
75972fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
76072fd7236SJulien Thierry 		break;
76172fd7236SJulien Thierry 	default:
76272fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
76372fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
76472fd7236SJulien Thierry 	}
76572fd7236SJulien Thierry 
76672fd7236SJulien Thierry 	/* We can't encode more than a 24bit value (12bit + 12bit shift) */
76772fd7236SJulien Thierry 	if (imm & ~(BIT(24) - 1))
76872fd7236SJulien Thierry 		goto out;
76972fd7236SJulien Thierry 
77072fd7236SJulien Thierry 	/* If we have something in the top 12 bits... */
77172fd7236SJulien Thierry 	if (imm & ~(SZ_4K - 1)) {
77272fd7236SJulien Thierry 		/* ... and in the low 12 bits -> error */
77372fd7236SJulien Thierry 		if (imm & (SZ_4K - 1))
77472fd7236SJulien Thierry 			goto out;
77572fd7236SJulien Thierry 
77672fd7236SJulien Thierry 		imm >>= 12;
77772fd7236SJulien Thierry 		insn |= AARCH64_INSN_LSL_12;
77872fd7236SJulien Thierry 	}
77972fd7236SJulien Thierry 
78072fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
78172fd7236SJulien Thierry 
78272fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
78372fd7236SJulien Thierry 
78472fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm);
78572fd7236SJulien Thierry 
78672fd7236SJulien Thierry out:
78772fd7236SJulien Thierry 	pr_err("%s: invalid immediate encoding %d\n", __func__, imm);
78872fd7236SJulien Thierry 	return AARCH64_BREAK_FAULT;
78972fd7236SJulien Thierry }
79072fd7236SJulien Thierry 
aarch64_insn_gen_bitfield(enum aarch64_insn_register dst,enum aarch64_insn_register src,int immr,int imms,enum aarch64_insn_variant variant,enum aarch64_insn_bitfield_type type)79172fd7236SJulien Thierry u32 aarch64_insn_gen_bitfield(enum aarch64_insn_register dst,
79272fd7236SJulien Thierry 			      enum aarch64_insn_register src,
79372fd7236SJulien Thierry 			      int immr, int imms,
79472fd7236SJulien Thierry 			      enum aarch64_insn_variant variant,
79572fd7236SJulien Thierry 			      enum aarch64_insn_bitfield_type type)
79672fd7236SJulien Thierry {
79772fd7236SJulien Thierry 	u32 insn;
79872fd7236SJulien Thierry 	u32 mask;
79972fd7236SJulien Thierry 
80072fd7236SJulien Thierry 	switch (type) {
80172fd7236SJulien Thierry 	case AARCH64_INSN_BITFIELD_MOVE:
80272fd7236SJulien Thierry 		insn = aarch64_insn_get_bfm_value();
80372fd7236SJulien Thierry 		break;
80472fd7236SJulien Thierry 	case AARCH64_INSN_BITFIELD_MOVE_UNSIGNED:
80572fd7236SJulien Thierry 		insn = aarch64_insn_get_ubfm_value();
80672fd7236SJulien Thierry 		break;
80772fd7236SJulien Thierry 	case AARCH64_INSN_BITFIELD_MOVE_SIGNED:
80872fd7236SJulien Thierry 		insn = aarch64_insn_get_sbfm_value();
80972fd7236SJulien Thierry 		break;
81072fd7236SJulien Thierry 	default:
81172fd7236SJulien Thierry 		pr_err("%s: unknown bitfield encoding %d\n", __func__, type);
81272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
81372fd7236SJulien Thierry 	}
81472fd7236SJulien Thierry 
81572fd7236SJulien Thierry 	switch (variant) {
81672fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
81772fd7236SJulien Thierry 		mask = GENMASK(4, 0);
81872fd7236SJulien Thierry 		break;
81972fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
82072fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT | AARCH64_INSN_N_BIT;
82172fd7236SJulien Thierry 		mask = GENMASK(5, 0);
82272fd7236SJulien Thierry 		break;
82372fd7236SJulien Thierry 	default:
82472fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
82572fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
82672fd7236SJulien Thierry 	}
82772fd7236SJulien Thierry 
82872fd7236SJulien Thierry 	if (immr & ~mask) {
82972fd7236SJulien Thierry 		pr_err("%s: invalid immr encoding %d\n", __func__, immr);
83072fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
83172fd7236SJulien Thierry 	}
83272fd7236SJulien Thierry 	if (imms & ~mask) {
83372fd7236SJulien Thierry 		pr_err("%s: invalid imms encoding %d\n", __func__, imms);
83472fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
83572fd7236SJulien Thierry 	}
83672fd7236SJulien Thierry 
83772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
83872fd7236SJulien Thierry 
83972fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
84072fd7236SJulien Thierry 
84172fd7236SJulien Thierry 	insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr);
84272fd7236SJulien Thierry 
84372fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms);
84472fd7236SJulien Thierry }
84572fd7236SJulien Thierry 
aarch64_insn_gen_movewide(enum aarch64_insn_register dst,int imm,int shift,enum aarch64_insn_variant variant,enum aarch64_insn_movewide_type type)84672fd7236SJulien Thierry u32 aarch64_insn_gen_movewide(enum aarch64_insn_register dst,
84772fd7236SJulien Thierry 			      int imm, int shift,
84872fd7236SJulien Thierry 			      enum aarch64_insn_variant variant,
84972fd7236SJulien Thierry 			      enum aarch64_insn_movewide_type type)
85072fd7236SJulien Thierry {
85172fd7236SJulien Thierry 	u32 insn;
85272fd7236SJulien Thierry 
85372fd7236SJulien Thierry 	switch (type) {
85472fd7236SJulien Thierry 	case AARCH64_INSN_MOVEWIDE_ZERO:
85572fd7236SJulien Thierry 		insn = aarch64_insn_get_movz_value();
85672fd7236SJulien Thierry 		break;
85772fd7236SJulien Thierry 	case AARCH64_INSN_MOVEWIDE_KEEP:
85872fd7236SJulien Thierry 		insn = aarch64_insn_get_movk_value();
85972fd7236SJulien Thierry 		break;
86072fd7236SJulien Thierry 	case AARCH64_INSN_MOVEWIDE_INVERSE:
86172fd7236SJulien Thierry 		insn = aarch64_insn_get_movn_value();
86272fd7236SJulien Thierry 		break;
86372fd7236SJulien Thierry 	default:
86472fd7236SJulien Thierry 		pr_err("%s: unknown movewide encoding %d\n", __func__, type);
86572fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
86672fd7236SJulien Thierry 	}
86772fd7236SJulien Thierry 
86872fd7236SJulien Thierry 	if (imm & ~(SZ_64K - 1)) {
86972fd7236SJulien Thierry 		pr_err("%s: invalid immediate encoding %d\n", __func__, imm);
87072fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
87172fd7236SJulien Thierry 	}
87272fd7236SJulien Thierry 
87372fd7236SJulien Thierry 	switch (variant) {
87472fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
87572fd7236SJulien Thierry 		if (shift != 0 && shift != 16) {
87672fd7236SJulien Thierry 			pr_err("%s: invalid shift encoding %d\n", __func__,
87772fd7236SJulien Thierry 			       shift);
87872fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
87972fd7236SJulien Thierry 		}
88072fd7236SJulien Thierry 		break;
88172fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
88272fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
88372fd7236SJulien Thierry 		if (shift != 0 && shift != 16 && shift != 32 && shift != 48) {
88472fd7236SJulien Thierry 			pr_err("%s: invalid shift encoding %d\n", __func__,
88572fd7236SJulien Thierry 			       shift);
88672fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
88772fd7236SJulien Thierry 		}
88872fd7236SJulien Thierry 		break;
88972fd7236SJulien Thierry 	default:
89072fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
89172fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
89272fd7236SJulien Thierry 	}
89372fd7236SJulien Thierry 
89472fd7236SJulien Thierry 	insn |= (shift >> 4) << 21;
89572fd7236SJulien Thierry 
89672fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
89772fd7236SJulien Thierry 
89872fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm);
89972fd7236SJulien Thierry }
90072fd7236SJulien Thierry 
aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst,enum aarch64_insn_register src,enum aarch64_insn_register reg,int shift,enum aarch64_insn_variant variant,enum aarch64_insn_adsb_type type)90172fd7236SJulien Thierry u32 aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst,
90272fd7236SJulien Thierry 					 enum aarch64_insn_register src,
90372fd7236SJulien Thierry 					 enum aarch64_insn_register reg,
90472fd7236SJulien Thierry 					 int shift,
90572fd7236SJulien Thierry 					 enum aarch64_insn_variant variant,
90672fd7236SJulien Thierry 					 enum aarch64_insn_adsb_type type)
90772fd7236SJulien Thierry {
90872fd7236SJulien Thierry 	u32 insn;
90972fd7236SJulien Thierry 
91072fd7236SJulien Thierry 	switch (type) {
91172fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_ADD:
91272fd7236SJulien Thierry 		insn = aarch64_insn_get_add_value();
91372fd7236SJulien Thierry 		break;
91472fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_SUB:
91572fd7236SJulien Thierry 		insn = aarch64_insn_get_sub_value();
91672fd7236SJulien Thierry 		break;
91772fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_ADD_SETFLAGS:
91872fd7236SJulien Thierry 		insn = aarch64_insn_get_adds_value();
91972fd7236SJulien Thierry 		break;
92072fd7236SJulien Thierry 	case AARCH64_INSN_ADSB_SUB_SETFLAGS:
92172fd7236SJulien Thierry 		insn = aarch64_insn_get_subs_value();
92272fd7236SJulien Thierry 		break;
92372fd7236SJulien Thierry 	default:
92472fd7236SJulien Thierry 		pr_err("%s: unknown add/sub encoding %d\n", __func__, type);
92572fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
92672fd7236SJulien Thierry 	}
92772fd7236SJulien Thierry 
92872fd7236SJulien Thierry 	switch (variant) {
92972fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
93072fd7236SJulien Thierry 		if (shift & ~(SZ_32 - 1)) {
93172fd7236SJulien Thierry 			pr_err("%s: invalid shift encoding %d\n", __func__,
93272fd7236SJulien Thierry 			       shift);
93372fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
93472fd7236SJulien Thierry 		}
93572fd7236SJulien Thierry 		break;
93672fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
93772fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
93872fd7236SJulien Thierry 		if (shift & ~(SZ_64 - 1)) {
93972fd7236SJulien Thierry 			pr_err("%s: invalid shift encoding %d\n", __func__,
94072fd7236SJulien Thierry 			       shift);
94172fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
94272fd7236SJulien Thierry 		}
94372fd7236SJulien Thierry 		break;
94472fd7236SJulien Thierry 	default:
94572fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
94672fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
94772fd7236SJulien Thierry 	}
94872fd7236SJulien Thierry 
94972fd7236SJulien Thierry 
95072fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
95172fd7236SJulien Thierry 
95272fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
95372fd7236SJulien Thierry 
95472fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
95572fd7236SJulien Thierry 
95672fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
95772fd7236SJulien Thierry }
95872fd7236SJulien Thierry 
aarch64_insn_gen_data1(enum aarch64_insn_register dst,enum aarch64_insn_register src,enum aarch64_insn_variant variant,enum aarch64_insn_data1_type type)95972fd7236SJulien Thierry u32 aarch64_insn_gen_data1(enum aarch64_insn_register dst,
96072fd7236SJulien Thierry 			   enum aarch64_insn_register src,
96172fd7236SJulien Thierry 			   enum aarch64_insn_variant variant,
96272fd7236SJulien Thierry 			   enum aarch64_insn_data1_type type)
96372fd7236SJulien Thierry {
96472fd7236SJulien Thierry 	u32 insn;
96572fd7236SJulien Thierry 
96672fd7236SJulien Thierry 	switch (type) {
96772fd7236SJulien Thierry 	case AARCH64_INSN_DATA1_REVERSE_16:
96872fd7236SJulien Thierry 		insn = aarch64_insn_get_rev16_value();
96972fd7236SJulien Thierry 		break;
97072fd7236SJulien Thierry 	case AARCH64_INSN_DATA1_REVERSE_32:
97172fd7236SJulien Thierry 		insn = aarch64_insn_get_rev32_value();
97272fd7236SJulien Thierry 		break;
97372fd7236SJulien Thierry 	case AARCH64_INSN_DATA1_REVERSE_64:
97472fd7236SJulien Thierry 		if (variant != AARCH64_INSN_VARIANT_64BIT) {
97572fd7236SJulien Thierry 			pr_err("%s: invalid variant for reverse64 %d\n",
97672fd7236SJulien Thierry 			       __func__, variant);
97772fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
97872fd7236SJulien Thierry 		}
97972fd7236SJulien Thierry 		insn = aarch64_insn_get_rev64_value();
98072fd7236SJulien Thierry 		break;
98172fd7236SJulien Thierry 	default:
98272fd7236SJulien Thierry 		pr_err("%s: unknown data1 encoding %d\n", __func__, type);
98372fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
98472fd7236SJulien Thierry 	}
98572fd7236SJulien Thierry 
98672fd7236SJulien Thierry 	switch (variant) {
98772fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
98872fd7236SJulien Thierry 		break;
98972fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
99072fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
99172fd7236SJulien Thierry 		break;
99272fd7236SJulien Thierry 	default:
99372fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
99472fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
99572fd7236SJulien Thierry 	}
99672fd7236SJulien Thierry 
99772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
99872fd7236SJulien Thierry 
99972fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
100072fd7236SJulien Thierry }
100172fd7236SJulien Thierry 
aarch64_insn_gen_data2(enum aarch64_insn_register dst,enum aarch64_insn_register src,enum aarch64_insn_register reg,enum aarch64_insn_variant variant,enum aarch64_insn_data2_type type)100272fd7236SJulien Thierry u32 aarch64_insn_gen_data2(enum aarch64_insn_register dst,
100372fd7236SJulien Thierry 			   enum aarch64_insn_register src,
100472fd7236SJulien Thierry 			   enum aarch64_insn_register reg,
100572fd7236SJulien Thierry 			   enum aarch64_insn_variant variant,
100672fd7236SJulien Thierry 			   enum aarch64_insn_data2_type type)
100772fd7236SJulien Thierry {
100872fd7236SJulien Thierry 	u32 insn;
100972fd7236SJulien Thierry 
101072fd7236SJulien Thierry 	switch (type) {
101172fd7236SJulien Thierry 	case AARCH64_INSN_DATA2_UDIV:
101272fd7236SJulien Thierry 		insn = aarch64_insn_get_udiv_value();
101372fd7236SJulien Thierry 		break;
101472fd7236SJulien Thierry 	case AARCH64_INSN_DATA2_SDIV:
101572fd7236SJulien Thierry 		insn = aarch64_insn_get_sdiv_value();
101672fd7236SJulien Thierry 		break;
101772fd7236SJulien Thierry 	case AARCH64_INSN_DATA2_LSLV:
101872fd7236SJulien Thierry 		insn = aarch64_insn_get_lslv_value();
101972fd7236SJulien Thierry 		break;
102072fd7236SJulien Thierry 	case AARCH64_INSN_DATA2_LSRV:
102172fd7236SJulien Thierry 		insn = aarch64_insn_get_lsrv_value();
102272fd7236SJulien Thierry 		break;
102372fd7236SJulien Thierry 	case AARCH64_INSN_DATA2_ASRV:
102472fd7236SJulien Thierry 		insn = aarch64_insn_get_asrv_value();
102572fd7236SJulien Thierry 		break;
102672fd7236SJulien Thierry 	case AARCH64_INSN_DATA2_RORV:
102772fd7236SJulien Thierry 		insn = aarch64_insn_get_rorv_value();
102872fd7236SJulien Thierry 		break;
102972fd7236SJulien Thierry 	default:
103072fd7236SJulien Thierry 		pr_err("%s: unknown data2 encoding %d\n", __func__, type);
103172fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
103272fd7236SJulien Thierry 	}
103372fd7236SJulien Thierry 
103472fd7236SJulien Thierry 	switch (variant) {
103572fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
103672fd7236SJulien Thierry 		break;
103772fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
103872fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
103972fd7236SJulien Thierry 		break;
104072fd7236SJulien Thierry 	default:
104172fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
104272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
104372fd7236SJulien Thierry 	}
104472fd7236SJulien Thierry 
104572fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
104672fd7236SJulien Thierry 
104772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
104872fd7236SJulien Thierry 
104972fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
105072fd7236SJulien Thierry }
105172fd7236SJulien Thierry 
aarch64_insn_gen_data3(enum aarch64_insn_register dst,enum aarch64_insn_register src,enum aarch64_insn_register reg1,enum aarch64_insn_register reg2,enum aarch64_insn_variant variant,enum aarch64_insn_data3_type type)105272fd7236SJulien Thierry u32 aarch64_insn_gen_data3(enum aarch64_insn_register dst,
105372fd7236SJulien Thierry 			   enum aarch64_insn_register src,
105472fd7236SJulien Thierry 			   enum aarch64_insn_register reg1,
105572fd7236SJulien Thierry 			   enum aarch64_insn_register reg2,
105672fd7236SJulien Thierry 			   enum aarch64_insn_variant variant,
105772fd7236SJulien Thierry 			   enum aarch64_insn_data3_type type)
105872fd7236SJulien Thierry {
105972fd7236SJulien Thierry 	u32 insn;
106072fd7236SJulien Thierry 
106172fd7236SJulien Thierry 	switch (type) {
106272fd7236SJulien Thierry 	case AARCH64_INSN_DATA3_MADD:
106372fd7236SJulien Thierry 		insn = aarch64_insn_get_madd_value();
106472fd7236SJulien Thierry 		break;
106572fd7236SJulien Thierry 	case AARCH64_INSN_DATA3_MSUB:
106672fd7236SJulien Thierry 		insn = aarch64_insn_get_msub_value();
106772fd7236SJulien Thierry 		break;
106872fd7236SJulien Thierry 	default:
106972fd7236SJulien Thierry 		pr_err("%s: unknown data3 encoding %d\n", __func__, type);
107072fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
107172fd7236SJulien Thierry 	}
107272fd7236SJulien Thierry 
107372fd7236SJulien Thierry 	switch (variant) {
107472fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
107572fd7236SJulien Thierry 		break;
107672fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
107772fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
107872fd7236SJulien Thierry 		break;
107972fd7236SJulien Thierry 	default:
108072fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
108172fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
108272fd7236SJulien Thierry 	}
108372fd7236SJulien Thierry 
108472fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
108572fd7236SJulien Thierry 
108672fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RA, insn, src);
108772fd7236SJulien Thierry 
108872fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
108972fd7236SJulien Thierry 					    reg1);
109072fd7236SJulien Thierry 
109172fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn,
109272fd7236SJulien Thierry 					    reg2);
109372fd7236SJulien Thierry }
109472fd7236SJulien Thierry 
aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,enum aarch64_insn_register src,enum aarch64_insn_register reg,int shift,enum aarch64_insn_variant variant,enum aarch64_insn_logic_type type)109572fd7236SJulien Thierry u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,
109672fd7236SJulien Thierry 					 enum aarch64_insn_register src,
109772fd7236SJulien Thierry 					 enum aarch64_insn_register reg,
109872fd7236SJulien Thierry 					 int shift,
109972fd7236SJulien Thierry 					 enum aarch64_insn_variant variant,
110072fd7236SJulien Thierry 					 enum aarch64_insn_logic_type type)
110172fd7236SJulien Thierry {
110272fd7236SJulien Thierry 	u32 insn;
110372fd7236SJulien Thierry 
110472fd7236SJulien Thierry 	switch (type) {
110572fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_AND:
110672fd7236SJulien Thierry 		insn = aarch64_insn_get_and_value();
110772fd7236SJulien Thierry 		break;
110872fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_BIC:
110972fd7236SJulien Thierry 		insn = aarch64_insn_get_bic_value();
111072fd7236SJulien Thierry 		break;
111172fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_ORR:
111272fd7236SJulien Thierry 		insn = aarch64_insn_get_orr_value();
111372fd7236SJulien Thierry 		break;
111472fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_ORN:
111572fd7236SJulien Thierry 		insn = aarch64_insn_get_orn_value();
111672fd7236SJulien Thierry 		break;
111772fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_EOR:
111872fd7236SJulien Thierry 		insn = aarch64_insn_get_eor_value();
111972fd7236SJulien Thierry 		break;
112072fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_EON:
112172fd7236SJulien Thierry 		insn = aarch64_insn_get_eon_value();
112272fd7236SJulien Thierry 		break;
112372fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_AND_SETFLAGS:
112472fd7236SJulien Thierry 		insn = aarch64_insn_get_ands_value();
112572fd7236SJulien Thierry 		break;
112672fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_BIC_SETFLAGS:
112772fd7236SJulien Thierry 		insn = aarch64_insn_get_bics_value();
112872fd7236SJulien Thierry 		break;
112972fd7236SJulien Thierry 	default:
113072fd7236SJulien Thierry 		pr_err("%s: unknown logical encoding %d\n", __func__, type);
113172fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
113272fd7236SJulien Thierry 	}
113372fd7236SJulien Thierry 
113472fd7236SJulien Thierry 	switch (variant) {
113572fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
113672fd7236SJulien Thierry 		if (shift & ~(SZ_32 - 1)) {
113772fd7236SJulien Thierry 			pr_err("%s: invalid shift encoding %d\n", __func__,
113872fd7236SJulien Thierry 			       shift);
113972fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
114072fd7236SJulien Thierry 		}
114172fd7236SJulien Thierry 		break;
114272fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
114372fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
114472fd7236SJulien Thierry 		if (shift & ~(SZ_64 - 1)) {
114572fd7236SJulien Thierry 			pr_err("%s: invalid shift encoding %d\n", __func__,
114672fd7236SJulien Thierry 			       shift);
114772fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
114872fd7236SJulien Thierry 		}
114972fd7236SJulien Thierry 		break;
115072fd7236SJulien Thierry 	default:
115172fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
115272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
115372fd7236SJulien Thierry 	}
115472fd7236SJulien Thierry 
115572fd7236SJulien Thierry 
115672fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst);
115772fd7236SJulien Thierry 
115872fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src);
115972fd7236SJulien Thierry 
116072fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg);
116172fd7236SJulien Thierry 
116272fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
116372fd7236SJulien Thierry }
116472fd7236SJulien Thierry 
116572fd7236SJulien Thierry /*
116672fd7236SJulien Thierry  * MOV (register) is architecturally an alias of ORR (shifted register) where
116772fd7236SJulien Thierry  * MOV <*d>, <*m> is equivalent to ORR <*d>, <*ZR>, <*m>
116872fd7236SJulien Thierry  */
aarch64_insn_gen_move_reg(enum aarch64_insn_register dst,enum aarch64_insn_register src,enum aarch64_insn_variant variant)116972fd7236SJulien Thierry u32 aarch64_insn_gen_move_reg(enum aarch64_insn_register dst,
117072fd7236SJulien Thierry 			      enum aarch64_insn_register src,
117172fd7236SJulien Thierry 			      enum aarch64_insn_variant variant)
117272fd7236SJulien Thierry {
117372fd7236SJulien Thierry 	return aarch64_insn_gen_logical_shifted_reg(dst, AARCH64_INSN_REG_ZR,
117472fd7236SJulien Thierry 						    src, 0, variant,
117572fd7236SJulien Thierry 						    AARCH64_INSN_LOGIC_ORR);
117672fd7236SJulien Thierry }
117772fd7236SJulien Thierry 
aarch64_insn_gen_adr(unsigned long pc,unsigned long addr,enum aarch64_insn_register reg,enum aarch64_insn_adr_type type)117872fd7236SJulien Thierry u32 aarch64_insn_gen_adr(unsigned long pc, unsigned long addr,
117972fd7236SJulien Thierry 			 enum aarch64_insn_register reg,
118072fd7236SJulien Thierry 			 enum aarch64_insn_adr_type type)
118172fd7236SJulien Thierry {
118272fd7236SJulien Thierry 	u32 insn;
118372fd7236SJulien Thierry 	s32 offset;
118472fd7236SJulien Thierry 
118572fd7236SJulien Thierry 	switch (type) {
118672fd7236SJulien Thierry 	case AARCH64_INSN_ADR_TYPE_ADR:
118772fd7236SJulien Thierry 		insn = aarch64_insn_get_adr_value();
118872fd7236SJulien Thierry 		offset = addr - pc;
118972fd7236SJulien Thierry 		break;
119072fd7236SJulien Thierry 	case AARCH64_INSN_ADR_TYPE_ADRP:
119172fd7236SJulien Thierry 		insn = aarch64_insn_get_adrp_value();
119272fd7236SJulien Thierry 		offset = (addr - ALIGN_DOWN(pc, SZ_4K)) >> 12;
119372fd7236SJulien Thierry 		break;
119472fd7236SJulien Thierry 	default:
119572fd7236SJulien Thierry 		pr_err("%s: unknown adr encoding %d\n", __func__, type);
119672fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
119772fd7236SJulien Thierry 	}
119872fd7236SJulien Thierry 
119972fd7236SJulien Thierry 	if (offset < -SZ_1M || offset >= SZ_1M)
120072fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
120172fd7236SJulien Thierry 
120272fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, reg);
120372fd7236SJulien Thierry 
120472fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_ADR, insn, offset);
120572fd7236SJulien Thierry }
120672fd7236SJulien Thierry 
120772fd7236SJulien Thierry /*
120872fd7236SJulien Thierry  * Decode the imm field of a branch, and return the byte offset as a
120972fd7236SJulien Thierry  * signed value (so it can be used when computing a new branch
121072fd7236SJulien Thierry  * target).
121172fd7236SJulien Thierry  */
aarch64_get_branch_offset(u32 insn)121272fd7236SJulien Thierry s32 aarch64_get_branch_offset(u32 insn)
121372fd7236SJulien Thierry {
121472fd7236SJulien Thierry 	s32 imm;
121572fd7236SJulien Thierry 
121672fd7236SJulien Thierry 	if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) {
121772fd7236SJulien Thierry 		imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn);
121872fd7236SJulien Thierry 		return (imm << 6) >> 4;
121972fd7236SJulien Thierry 	}
122072fd7236SJulien Thierry 
122172fd7236SJulien Thierry 	if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
122272fd7236SJulien Thierry 	    aarch64_insn_is_bcond(insn)) {
122372fd7236SJulien Thierry 		imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_19, insn);
122472fd7236SJulien Thierry 		return (imm << 13) >> 11;
122572fd7236SJulien Thierry 	}
122672fd7236SJulien Thierry 
122772fd7236SJulien Thierry 	if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) {
122872fd7236SJulien Thierry 		imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_14, insn);
122972fd7236SJulien Thierry 		return (imm << 18) >> 16;
123072fd7236SJulien Thierry 	}
123172fd7236SJulien Thierry 
123272fd7236SJulien Thierry 	/* Unhandled instruction */
123372fd7236SJulien Thierry 	BUG();
123472fd7236SJulien Thierry }
123572fd7236SJulien Thierry 
123672fd7236SJulien Thierry /*
123772fd7236SJulien Thierry  * Encode the displacement of a branch in the imm field and return the
123872fd7236SJulien Thierry  * updated instruction.
123972fd7236SJulien Thierry  */
aarch64_set_branch_offset(u32 insn,s32 offset)124072fd7236SJulien Thierry u32 aarch64_set_branch_offset(u32 insn, s32 offset)
124172fd7236SJulien Thierry {
124272fd7236SJulien Thierry 	if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn))
124372fd7236SJulien Thierry 		return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
124472fd7236SJulien Thierry 						     offset >> 2);
124572fd7236SJulien Thierry 
124672fd7236SJulien Thierry 	if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
124772fd7236SJulien Thierry 	    aarch64_insn_is_bcond(insn))
124872fd7236SJulien Thierry 		return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
124972fd7236SJulien Thierry 						     offset >> 2);
125072fd7236SJulien Thierry 
125172fd7236SJulien Thierry 	if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn))
125272fd7236SJulien Thierry 		return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_14, insn,
125372fd7236SJulien Thierry 						     offset >> 2);
125472fd7236SJulien Thierry 
125572fd7236SJulien Thierry 	/* Unhandled instruction */
125672fd7236SJulien Thierry 	BUG();
125772fd7236SJulien Thierry }
125872fd7236SJulien Thierry 
aarch64_insn_adrp_get_offset(u32 insn)125972fd7236SJulien Thierry s32 aarch64_insn_adrp_get_offset(u32 insn)
126072fd7236SJulien Thierry {
126172fd7236SJulien Thierry 	BUG_ON(!aarch64_insn_is_adrp(insn));
126272fd7236SJulien Thierry 	return aarch64_insn_decode_immediate(AARCH64_INSN_IMM_ADR, insn) << 12;
126372fd7236SJulien Thierry }
126472fd7236SJulien Thierry 
aarch64_insn_adrp_set_offset(u32 insn,s32 offset)126572fd7236SJulien Thierry u32 aarch64_insn_adrp_set_offset(u32 insn, s32 offset)
126672fd7236SJulien Thierry {
126772fd7236SJulien Thierry 	BUG_ON(!aarch64_insn_is_adrp(insn));
126872fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_ADR, insn,
126972fd7236SJulien Thierry 						offset >> 12);
127072fd7236SJulien Thierry }
127172fd7236SJulien Thierry 
127272fd7236SJulien Thierry /*
127372fd7236SJulien Thierry  * Extract the Op/CR data from a msr/mrs instruction.
127472fd7236SJulien Thierry  */
aarch64_insn_extract_system_reg(u32 insn)127572fd7236SJulien Thierry u32 aarch64_insn_extract_system_reg(u32 insn)
127672fd7236SJulien Thierry {
127772fd7236SJulien Thierry 	return (insn & 0x1FFFE0) >> 5;
127872fd7236SJulien Thierry }
127972fd7236SJulien Thierry 
aarch32_insn_is_wide(u32 insn)128072fd7236SJulien Thierry bool aarch32_insn_is_wide(u32 insn)
128172fd7236SJulien Thierry {
128272fd7236SJulien Thierry 	return insn >= 0xe800;
128372fd7236SJulien Thierry }
128472fd7236SJulien Thierry 
128572fd7236SJulien Thierry /*
128672fd7236SJulien Thierry  * Macros/defines for extracting register numbers from instruction.
128772fd7236SJulien Thierry  */
aarch32_insn_extract_reg_num(u32 insn,int offset)128872fd7236SJulien Thierry u32 aarch32_insn_extract_reg_num(u32 insn, int offset)
128972fd7236SJulien Thierry {
129072fd7236SJulien Thierry 	return (insn & (0xf << offset)) >> offset;
129172fd7236SJulien Thierry }
129272fd7236SJulien Thierry 
129372fd7236SJulien Thierry #define OPC2_MASK	0x7
129472fd7236SJulien Thierry #define OPC2_OFFSET	5
aarch32_insn_mcr_extract_opc2(u32 insn)129572fd7236SJulien Thierry u32 aarch32_insn_mcr_extract_opc2(u32 insn)
129672fd7236SJulien Thierry {
129772fd7236SJulien Thierry 	return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET;
129872fd7236SJulien Thierry }
129972fd7236SJulien Thierry 
130072fd7236SJulien Thierry #define CRM_MASK	0xf
aarch32_insn_mcr_extract_crm(u32 insn)130172fd7236SJulien Thierry u32 aarch32_insn_mcr_extract_crm(u32 insn)
130272fd7236SJulien Thierry {
130372fd7236SJulien Thierry 	return insn & CRM_MASK;
130472fd7236SJulien Thierry }
130572fd7236SJulien Thierry 
range_of_ones(u64 val)130672fd7236SJulien Thierry static bool range_of_ones(u64 val)
130772fd7236SJulien Thierry {
130872fd7236SJulien Thierry 	/* Doesn't handle full ones or full zeroes */
130972fd7236SJulien Thierry 	u64 sval = val >> __ffs64(val);
131072fd7236SJulien Thierry 
131172fd7236SJulien Thierry 	/* One of Sean Eron Anderson's bithack tricks */
131272fd7236SJulien Thierry 	return ((sval + 1) & (sval)) == 0;
131372fd7236SJulien Thierry }
131472fd7236SJulien Thierry 
aarch64_encode_immediate(u64 imm,enum aarch64_insn_variant variant,u32 insn)131572fd7236SJulien Thierry static u32 aarch64_encode_immediate(u64 imm,
131672fd7236SJulien Thierry 				    enum aarch64_insn_variant variant,
131772fd7236SJulien Thierry 				    u32 insn)
131872fd7236SJulien Thierry {
131972fd7236SJulien Thierry 	unsigned int immr, imms, n, ones, ror, esz, tmp;
132072fd7236SJulien Thierry 	u64 mask;
132172fd7236SJulien Thierry 
132272fd7236SJulien Thierry 	switch (variant) {
132372fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
132472fd7236SJulien Thierry 		esz = 32;
132572fd7236SJulien Thierry 		break;
132672fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
132772fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
132872fd7236SJulien Thierry 		esz = 64;
132972fd7236SJulien Thierry 		break;
133072fd7236SJulien Thierry 	default:
133172fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
133272fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
133372fd7236SJulien Thierry 	}
133472fd7236SJulien Thierry 
133572fd7236SJulien Thierry 	mask = GENMASK(esz - 1, 0);
133672fd7236SJulien Thierry 
133772fd7236SJulien Thierry 	/* Can't encode full zeroes, full ones, or value wider than the mask */
133872fd7236SJulien Thierry 	if (!imm || imm == mask || imm & ~mask)
133972fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
134072fd7236SJulien Thierry 
134172fd7236SJulien Thierry 	/*
134272fd7236SJulien Thierry 	 * Inverse of Replicate(). Try to spot a repeating pattern
134372fd7236SJulien Thierry 	 * with a pow2 stride.
134472fd7236SJulien Thierry 	 */
134572fd7236SJulien Thierry 	for (tmp = esz / 2; tmp >= 2; tmp /= 2) {
134672fd7236SJulien Thierry 		u64 emask = BIT(tmp) - 1;
134772fd7236SJulien Thierry 
134872fd7236SJulien Thierry 		if ((imm & emask) != ((imm >> tmp) & emask))
134972fd7236SJulien Thierry 			break;
135072fd7236SJulien Thierry 
135172fd7236SJulien Thierry 		esz = tmp;
135272fd7236SJulien Thierry 		mask = emask;
135372fd7236SJulien Thierry 	}
135472fd7236SJulien Thierry 
135572fd7236SJulien Thierry 	/* N is only set if we're encoding a 64bit value */
135672fd7236SJulien Thierry 	n = esz == 64;
135772fd7236SJulien Thierry 
135872fd7236SJulien Thierry 	/* Trim imm to the element size */
135972fd7236SJulien Thierry 	imm &= mask;
136072fd7236SJulien Thierry 
136172fd7236SJulien Thierry 	/* That's how many ones we need to encode */
136272fd7236SJulien Thierry 	ones = hweight64(imm);
136372fd7236SJulien Thierry 
136472fd7236SJulien Thierry 	/*
136572fd7236SJulien Thierry 	 * imms is set to (ones - 1), prefixed with a string of ones
136672fd7236SJulien Thierry 	 * and a zero if they fit. Cap it to 6 bits.
136772fd7236SJulien Thierry 	 */
136872fd7236SJulien Thierry 	imms  = ones - 1;
136972fd7236SJulien Thierry 	imms |= 0xf << ffs(esz);
137072fd7236SJulien Thierry 	imms &= BIT(6) - 1;
137172fd7236SJulien Thierry 
137272fd7236SJulien Thierry 	/* Compute the rotation */
137372fd7236SJulien Thierry 	if (range_of_ones(imm)) {
137472fd7236SJulien Thierry 		/*
137572fd7236SJulien Thierry 		 * Pattern: 0..01..10..0
137672fd7236SJulien Thierry 		 *
137772fd7236SJulien Thierry 		 * Compute how many rotate we need to align it right
137872fd7236SJulien Thierry 		 */
137972fd7236SJulien Thierry 		ror = __ffs64(imm);
138072fd7236SJulien Thierry 	} else {
138172fd7236SJulien Thierry 		/*
138272fd7236SJulien Thierry 		 * Pattern: 0..01..10..01..1
138372fd7236SJulien Thierry 		 *
138472fd7236SJulien Thierry 		 * Fill the unused top bits with ones, and check if
138572fd7236SJulien Thierry 		 * the result is a valid immediate (all ones with a
138672fd7236SJulien Thierry 		 * contiguous ranges of zeroes).
138772fd7236SJulien Thierry 		 */
138872fd7236SJulien Thierry 		imm |= ~mask;
138972fd7236SJulien Thierry 		if (!range_of_ones(~imm))
139072fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
139172fd7236SJulien Thierry 
139272fd7236SJulien Thierry 		/*
139372fd7236SJulien Thierry 		 * Compute the rotation to get a continuous set of
139472fd7236SJulien Thierry 		 * ones, with the first bit set at position 0
139572fd7236SJulien Thierry 		 */
1396a6aab018SJames Morse 		ror = fls64(~imm);
139772fd7236SJulien Thierry 	}
139872fd7236SJulien Thierry 
139972fd7236SJulien Thierry 	/*
140072fd7236SJulien Thierry 	 * immr is the number of bits we need to rotate back to the
140172fd7236SJulien Thierry 	 * original set of ones. Note that this is relative to the
140272fd7236SJulien Thierry 	 * element size...
140372fd7236SJulien Thierry 	 */
140472fd7236SJulien Thierry 	immr = (esz - ror) % esz;
140572fd7236SJulien Thierry 
140672fd7236SJulien Thierry 	insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, n);
140772fd7236SJulien Thierry 	insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr);
140872fd7236SJulien Thierry 	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms);
140972fd7236SJulien Thierry }
141072fd7236SJulien Thierry 
aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,enum aarch64_insn_variant variant,enum aarch64_insn_register Rn,enum aarch64_insn_register Rd,u64 imm)141172fd7236SJulien Thierry u32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type,
141272fd7236SJulien Thierry 				       enum aarch64_insn_variant variant,
141372fd7236SJulien Thierry 				       enum aarch64_insn_register Rn,
141472fd7236SJulien Thierry 				       enum aarch64_insn_register Rd,
141572fd7236SJulien Thierry 				       u64 imm)
141672fd7236SJulien Thierry {
141772fd7236SJulien Thierry 	u32 insn;
141872fd7236SJulien Thierry 
141972fd7236SJulien Thierry 	switch (type) {
142072fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_AND:
142172fd7236SJulien Thierry 		insn = aarch64_insn_get_and_imm_value();
142272fd7236SJulien Thierry 		break;
142372fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_ORR:
142472fd7236SJulien Thierry 		insn = aarch64_insn_get_orr_imm_value();
142572fd7236SJulien Thierry 		break;
142672fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_EOR:
142772fd7236SJulien Thierry 		insn = aarch64_insn_get_eor_imm_value();
142872fd7236SJulien Thierry 		break;
142972fd7236SJulien Thierry 	case AARCH64_INSN_LOGIC_AND_SETFLAGS:
143072fd7236SJulien Thierry 		insn = aarch64_insn_get_ands_imm_value();
143172fd7236SJulien Thierry 		break;
143272fd7236SJulien Thierry 	default:
143372fd7236SJulien Thierry 		pr_err("%s: unknown logical encoding %d\n", __func__, type);
143472fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
143572fd7236SJulien Thierry 	}
143672fd7236SJulien Thierry 
143772fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd);
143872fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
143972fd7236SJulien Thierry 	return aarch64_encode_immediate(imm, variant, insn);
144072fd7236SJulien Thierry }
144172fd7236SJulien Thierry 
aarch64_insn_gen_extr(enum aarch64_insn_variant variant,enum aarch64_insn_register Rm,enum aarch64_insn_register Rn,enum aarch64_insn_register Rd,u8 lsb)144272fd7236SJulien Thierry u32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant,
144372fd7236SJulien Thierry 			  enum aarch64_insn_register Rm,
144472fd7236SJulien Thierry 			  enum aarch64_insn_register Rn,
144572fd7236SJulien Thierry 			  enum aarch64_insn_register Rd,
144672fd7236SJulien Thierry 			  u8 lsb)
144772fd7236SJulien Thierry {
144872fd7236SJulien Thierry 	u32 insn;
144972fd7236SJulien Thierry 
145072fd7236SJulien Thierry 	insn = aarch64_insn_get_extr_value();
145172fd7236SJulien Thierry 
145272fd7236SJulien Thierry 	switch (variant) {
145372fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_32BIT:
145472fd7236SJulien Thierry 		if (lsb > 31)
145572fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
145672fd7236SJulien Thierry 		break;
145772fd7236SJulien Thierry 	case AARCH64_INSN_VARIANT_64BIT:
145872fd7236SJulien Thierry 		if (lsb > 63)
145972fd7236SJulien Thierry 			return AARCH64_BREAK_FAULT;
146072fd7236SJulien Thierry 		insn |= AARCH64_INSN_SF_BIT;
146172fd7236SJulien Thierry 		insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, 1);
146272fd7236SJulien Thierry 		break;
146372fd7236SJulien Thierry 	default:
146472fd7236SJulien Thierry 		pr_err("%s: unknown variant encoding %d\n", __func__, variant);
146572fd7236SJulien Thierry 		return AARCH64_BREAK_FAULT;
146672fd7236SJulien Thierry 	}
146772fd7236SJulien Thierry 
146872fd7236SJulien Thierry 	insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, lsb);
146972fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd);
147072fd7236SJulien Thierry 	insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn);
147172fd7236SJulien Thierry 	return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, Rm);
147272fd7236SJulien Thierry }
1473fa1114d9SHou Tao 
aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type)1474fa1114d9SHou Tao u32 aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type)
1475fa1114d9SHou Tao {
1476fa1114d9SHou Tao 	u32 opt;
1477fa1114d9SHou Tao 	u32 insn;
1478fa1114d9SHou Tao 
1479fa1114d9SHou Tao 	switch (type) {
1480fa1114d9SHou Tao 	case AARCH64_INSN_MB_SY:
1481fa1114d9SHou Tao 		opt = 0xf;
1482fa1114d9SHou Tao 		break;
1483fa1114d9SHou Tao 	case AARCH64_INSN_MB_ST:
1484fa1114d9SHou Tao 		opt = 0xe;
1485fa1114d9SHou Tao 		break;
1486fa1114d9SHou Tao 	case AARCH64_INSN_MB_LD:
1487fa1114d9SHou Tao 		opt = 0xd;
1488fa1114d9SHou Tao 		break;
1489fa1114d9SHou Tao 	case AARCH64_INSN_MB_ISH:
1490fa1114d9SHou Tao 		opt = 0xb;
1491fa1114d9SHou Tao 		break;
1492fa1114d9SHou Tao 	case AARCH64_INSN_MB_ISHST:
1493fa1114d9SHou Tao 		opt = 0xa;
1494fa1114d9SHou Tao 		break;
1495fa1114d9SHou Tao 	case AARCH64_INSN_MB_ISHLD:
1496fa1114d9SHou Tao 		opt = 0x9;
1497fa1114d9SHou Tao 		break;
1498fa1114d9SHou Tao 	case AARCH64_INSN_MB_NSH:
1499fa1114d9SHou Tao 		opt = 0x7;
1500fa1114d9SHou Tao 		break;
1501fa1114d9SHou Tao 	case AARCH64_INSN_MB_NSHST:
1502fa1114d9SHou Tao 		opt = 0x6;
1503fa1114d9SHou Tao 		break;
1504fa1114d9SHou Tao 	case AARCH64_INSN_MB_NSHLD:
1505fa1114d9SHou Tao 		opt = 0x5;
1506fa1114d9SHou Tao 		break;
1507fa1114d9SHou Tao 	default:
1508fa1114d9SHou Tao 		pr_err("%s: unknown dmb type %d\n", __func__, type);
1509fa1114d9SHou Tao 		return AARCH64_BREAK_FAULT;
1510fa1114d9SHou Tao 	}
1511fa1114d9SHou Tao 
1512fa1114d9SHou Tao 	insn = aarch64_insn_get_dmb_value();
1513fa1114d9SHou Tao 	insn &= ~GENMASK(11, 8);
1514fa1114d9SHou Tao 	insn |= (opt << 8);
1515fa1114d9SHou Tao 
1516fa1114d9SHou Tao 	return insn;
1517fa1114d9SHou Tao }
1518