162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Huawei Ltd. 462306a36Sopenharmony_ci * Author: Jiang Liu <liuj97@gmail.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@gmail.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/bitops.h> 962306a36Sopenharmony_ci#include <linux/bug.h> 1062306a36Sopenharmony_ci#include <linux/printk.h> 1162306a36Sopenharmony_ci#include <linux/sizes.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/debug-monitors.h> 1562306a36Sopenharmony_ci#include <asm/errno.h> 1662306a36Sopenharmony_ci#include <asm/insn.h> 1762306a36Sopenharmony_ci#include <asm/kprobes.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define AARCH64_INSN_SF_BIT BIT(31) 2062306a36Sopenharmony_ci#define AARCH64_INSN_N_BIT BIT(22) 2162306a36Sopenharmony_ci#define AARCH64_INSN_LSL_12 BIT(22) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type type, 2462306a36Sopenharmony_ci u32 *maskp, int *shiftp) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci u32 mask; 2762306a36Sopenharmony_ci int shift; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci switch (type) { 3062306a36Sopenharmony_ci case AARCH64_INSN_IMM_26: 3162306a36Sopenharmony_ci mask = BIT(26) - 1; 3262306a36Sopenharmony_ci shift = 0; 3362306a36Sopenharmony_ci break; 3462306a36Sopenharmony_ci case AARCH64_INSN_IMM_19: 3562306a36Sopenharmony_ci mask = BIT(19) - 1; 3662306a36Sopenharmony_ci shift = 5; 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci case AARCH64_INSN_IMM_16: 3962306a36Sopenharmony_ci mask = BIT(16) - 1; 4062306a36Sopenharmony_ci shift = 5; 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci case AARCH64_INSN_IMM_14: 4362306a36Sopenharmony_ci mask = BIT(14) - 1; 4462306a36Sopenharmony_ci shift = 5; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci case AARCH64_INSN_IMM_12: 4762306a36Sopenharmony_ci mask = BIT(12) - 1; 4862306a36Sopenharmony_ci shift = 10; 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci case AARCH64_INSN_IMM_9: 5162306a36Sopenharmony_ci mask = BIT(9) - 1; 5262306a36Sopenharmony_ci shift = 12; 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci case AARCH64_INSN_IMM_7: 5562306a36Sopenharmony_ci mask = BIT(7) - 1; 5662306a36Sopenharmony_ci shift = 15; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci case AARCH64_INSN_IMM_6: 5962306a36Sopenharmony_ci case AARCH64_INSN_IMM_S: 6062306a36Sopenharmony_ci mask = BIT(6) - 1; 6162306a36Sopenharmony_ci shift = 10; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case AARCH64_INSN_IMM_R: 6462306a36Sopenharmony_ci mask = BIT(6) - 1; 6562306a36Sopenharmony_ci shift = 16; 6662306a36Sopenharmony_ci break; 6762306a36Sopenharmony_ci case AARCH64_INSN_IMM_N: 6862306a36Sopenharmony_ci mask = 1; 6962306a36Sopenharmony_ci shift = 22; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci default: 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci *maskp = mask; 7662306a36Sopenharmony_ci *shiftp = shift; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define ADR_IMM_HILOSPLIT 2 8262306a36Sopenharmony_ci#define ADR_IMM_SIZE SZ_2M 8362306a36Sopenharmony_ci#define ADR_IMM_LOMASK ((1 << ADR_IMM_HILOSPLIT) - 1) 8462306a36Sopenharmony_ci#define ADR_IMM_HIMASK ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1) 8562306a36Sopenharmony_ci#define ADR_IMM_LOSHIFT 29 8662306a36Sopenharmony_ci#define ADR_IMM_HISHIFT 5 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciu64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci u32 immlo, immhi, mask; 9162306a36Sopenharmony_ci int shift; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci switch (type) { 9462306a36Sopenharmony_ci case AARCH64_INSN_IMM_ADR: 9562306a36Sopenharmony_ci shift = 0; 9662306a36Sopenharmony_ci immlo = (insn >> ADR_IMM_LOSHIFT) & ADR_IMM_LOMASK; 9762306a36Sopenharmony_ci immhi = (insn >> ADR_IMM_HISHIFT) & ADR_IMM_HIMASK; 9862306a36Sopenharmony_ci insn = (immhi << ADR_IMM_HILOSPLIT) | immlo; 9962306a36Sopenharmony_ci mask = ADR_IMM_SIZE - 1; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { 10362306a36Sopenharmony_ci pr_err("%s: unknown immediate encoding %d\n", __func__, 10462306a36Sopenharmony_ci type); 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return (insn >> shift) & mask; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciu32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, 11362306a36Sopenharmony_ci u32 insn, u64 imm) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci u32 immlo, immhi, mask; 11662306a36Sopenharmony_ci int shift; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (insn == AARCH64_BREAK_FAULT) 11962306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci switch (type) { 12262306a36Sopenharmony_ci case AARCH64_INSN_IMM_ADR: 12362306a36Sopenharmony_ci shift = 0; 12462306a36Sopenharmony_ci immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT; 12562306a36Sopenharmony_ci imm >>= ADR_IMM_HILOSPLIT; 12662306a36Sopenharmony_ci immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT; 12762306a36Sopenharmony_ci imm = immlo | immhi; 12862306a36Sopenharmony_ci mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) | 12962306a36Sopenharmony_ci (ADR_IMM_HIMASK << ADR_IMM_HISHIFT)); 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci default: 13262306a36Sopenharmony_ci if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) { 13362306a36Sopenharmony_ci pr_err("%s: unknown immediate encoding %d\n", __func__, 13462306a36Sopenharmony_ci type); 13562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Update the immediate field. */ 14062306a36Sopenharmony_ci insn &= ~(mask << shift); 14162306a36Sopenharmony_ci insn |= (imm & mask) << shift; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return insn; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciu32 aarch64_insn_decode_register(enum aarch64_insn_register_type type, 14762306a36Sopenharmony_ci u32 insn) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int shift; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci switch (type) { 15262306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RT: 15362306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RD: 15462306a36Sopenharmony_ci shift = 0; 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RN: 15762306a36Sopenharmony_ci shift = 5; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RT2: 16062306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RA: 16162306a36Sopenharmony_ci shift = 10; 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RM: 16462306a36Sopenharmony_ci shift = 16; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci default: 16762306a36Sopenharmony_ci pr_err("%s: unknown register type encoding %d\n", __func__, 16862306a36Sopenharmony_ci type); 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci return (insn >> shift) & GENMASK(4, 0); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type, 17662306a36Sopenharmony_ci u32 insn, 17762306a36Sopenharmony_ci enum aarch64_insn_register reg) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int shift; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (insn == AARCH64_BREAK_FAULT) 18262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (reg < AARCH64_INSN_REG_0 || reg > AARCH64_INSN_REG_SP) { 18562306a36Sopenharmony_ci pr_err("%s: unknown register encoding %d\n", __func__, reg); 18662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci switch (type) { 19062306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RT: 19162306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RD: 19262306a36Sopenharmony_ci shift = 0; 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RN: 19562306a36Sopenharmony_ci shift = 5; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RT2: 19862306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RA: 19962306a36Sopenharmony_ci shift = 10; 20062306a36Sopenharmony_ci break; 20162306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RM: 20262306a36Sopenharmony_ci case AARCH64_INSN_REGTYPE_RS: 20362306a36Sopenharmony_ci shift = 16; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci default: 20662306a36Sopenharmony_ci pr_err("%s: unknown register type encoding %d\n", __func__, 20762306a36Sopenharmony_ci type); 20862306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci insn &= ~(GENMASK(4, 0) << shift); 21262306a36Sopenharmony_ci insn |= reg << shift; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return insn; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const u32 aarch64_insn_ldst_size[] = { 21862306a36Sopenharmony_ci [AARCH64_INSN_SIZE_8] = 0, 21962306a36Sopenharmony_ci [AARCH64_INSN_SIZE_16] = 1, 22062306a36Sopenharmony_ci [AARCH64_INSN_SIZE_32] = 2, 22162306a36Sopenharmony_ci [AARCH64_INSN_SIZE_64] = 3, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type, 22562306a36Sopenharmony_ci u32 insn) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci u32 size; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (type < AARCH64_INSN_SIZE_8 || type > AARCH64_INSN_SIZE_64) { 23062306a36Sopenharmony_ci pr_err("%s: unknown size encoding %d\n", __func__, type); 23162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci size = aarch64_insn_ldst_size[type]; 23562306a36Sopenharmony_ci insn &= ~GENMASK(31, 30); 23662306a36Sopenharmony_ci insn |= size << 30; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return insn; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic inline long label_imm_common(unsigned long pc, unsigned long addr, 24262306a36Sopenharmony_ci long range) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci long offset; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if ((pc & 0x3) || (addr & 0x3)) { 24762306a36Sopenharmony_ci pr_err("%s: A64 instructions must be word aligned\n", __func__); 24862306a36Sopenharmony_ci return range; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci offset = ((long)addr - (long)pc); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (offset < -range || offset >= range) { 25462306a36Sopenharmony_ci pr_err("%s: offset out of range\n", __func__); 25562306a36Sopenharmony_ci return range; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci return offset; 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ciu32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, 26262306a36Sopenharmony_ci enum aarch64_insn_branch_type type) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci u32 insn; 26562306a36Sopenharmony_ci long offset; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * B/BL support [-128M, 128M) offset 26962306a36Sopenharmony_ci * ARM64 virtual address arrangement guarantees all kernel and module 27062306a36Sopenharmony_ci * texts are within +/-128M. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci offset = label_imm_common(pc, addr, SZ_128M); 27362306a36Sopenharmony_ci if (offset >= SZ_128M) 27462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci switch (type) { 27762306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_LINK: 27862306a36Sopenharmony_ci insn = aarch64_insn_get_bl_value(); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_NOLINK: 28162306a36Sopenharmony_ci insn = aarch64_insn_get_b_value(); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci default: 28462306a36Sopenharmony_ci pr_err("%s: unknown branch encoding %d\n", __func__, type); 28562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn, 28962306a36Sopenharmony_ci offset >> 2); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciu32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr, 29362306a36Sopenharmony_ci enum aarch64_insn_register reg, 29462306a36Sopenharmony_ci enum aarch64_insn_variant variant, 29562306a36Sopenharmony_ci enum aarch64_insn_branch_type type) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u32 insn; 29862306a36Sopenharmony_ci long offset; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci offset = label_imm_common(pc, addr, SZ_1M); 30162306a36Sopenharmony_ci if (offset >= SZ_1M) 30262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci switch (type) { 30562306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_COMP_ZERO: 30662306a36Sopenharmony_ci insn = aarch64_insn_get_cbz_value(); 30762306a36Sopenharmony_ci break; 30862306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_COMP_NONZERO: 30962306a36Sopenharmony_ci insn = aarch64_insn_get_cbnz_value(); 31062306a36Sopenharmony_ci break; 31162306a36Sopenharmony_ci default: 31262306a36Sopenharmony_ci pr_err("%s: unknown branch encoding %d\n", __func__, type); 31362306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci switch (variant) { 31762306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 32062306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci default: 32362306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 32462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, 33062306a36Sopenharmony_ci offset >> 2); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciu32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr, 33462306a36Sopenharmony_ci enum aarch64_insn_condition cond) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci u32 insn; 33762306a36Sopenharmony_ci long offset; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci offset = label_imm_common(pc, addr, SZ_1M); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci insn = aarch64_insn_get_bcond_value(); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (cond < AARCH64_INSN_COND_EQ || cond > AARCH64_INSN_COND_AL) { 34462306a36Sopenharmony_ci pr_err("%s: unknown condition encoding %d\n", __func__, cond); 34562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci insn |= cond; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, 35062306a36Sopenharmony_ci offset >> 2); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ciu32 aarch64_insn_gen_branch_reg(enum aarch64_insn_register reg, 35462306a36Sopenharmony_ci enum aarch64_insn_branch_type type) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci u32 insn; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci switch (type) { 35962306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_NOLINK: 36062306a36Sopenharmony_ci insn = aarch64_insn_get_br_value(); 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_LINK: 36362306a36Sopenharmony_ci insn = aarch64_insn_get_blr_value(); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case AARCH64_INSN_BRANCH_RETURN: 36662306a36Sopenharmony_ci insn = aarch64_insn_get_ret_value(); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci default: 36962306a36Sopenharmony_ci pr_err("%s: unknown branch encoding %d\n", __func__, type); 37062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, reg); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciu32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg, 37762306a36Sopenharmony_ci enum aarch64_insn_register base, 37862306a36Sopenharmony_ci enum aarch64_insn_register offset, 37962306a36Sopenharmony_ci enum aarch64_insn_size_type size, 38062306a36Sopenharmony_ci enum aarch64_insn_ldst_type type) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci u32 insn; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci switch (type) { 38562306a36Sopenharmony_ci case AARCH64_INSN_LDST_LOAD_REG_OFFSET: 38662306a36Sopenharmony_ci insn = aarch64_insn_get_ldr_reg_value(); 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci case AARCH64_INSN_LDST_SIGNED_LOAD_REG_OFFSET: 38962306a36Sopenharmony_ci insn = aarch64_insn_get_signed_ldr_reg_value(); 39062306a36Sopenharmony_ci break; 39162306a36Sopenharmony_ci case AARCH64_INSN_LDST_STORE_REG_OFFSET: 39262306a36Sopenharmony_ci insn = aarch64_insn_get_str_reg_value(); 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci default: 39562306a36Sopenharmony_ci pr_err("%s: unknown load/store encoding %d\n", __func__, type); 39662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci insn = aarch64_insn_encode_ldst_size(size, insn); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 40462306a36Sopenharmony_ci base); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, 40762306a36Sopenharmony_ci offset); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciu32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg, 41162306a36Sopenharmony_ci enum aarch64_insn_register base, 41262306a36Sopenharmony_ci unsigned int imm, 41362306a36Sopenharmony_ci enum aarch64_insn_size_type size, 41462306a36Sopenharmony_ci enum aarch64_insn_ldst_type type) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci u32 insn; 41762306a36Sopenharmony_ci u32 shift; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (size < AARCH64_INSN_SIZE_8 || size > AARCH64_INSN_SIZE_64) { 42062306a36Sopenharmony_ci pr_err("%s: unknown size encoding %d\n", __func__, type); 42162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci shift = aarch64_insn_ldst_size[size]; 42562306a36Sopenharmony_ci if (imm & ~(BIT(12 + shift) - BIT(shift))) { 42662306a36Sopenharmony_ci pr_err("%s: invalid imm: %d\n", __func__, imm); 42762306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci imm >>= shift; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci switch (type) { 43362306a36Sopenharmony_ci case AARCH64_INSN_LDST_LOAD_IMM_OFFSET: 43462306a36Sopenharmony_ci insn = aarch64_insn_get_ldr_imm_value(); 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci case AARCH64_INSN_LDST_SIGNED_LOAD_IMM_OFFSET: 43762306a36Sopenharmony_ci insn = aarch64_insn_get_signed_load_imm_value(); 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci case AARCH64_INSN_LDST_STORE_IMM_OFFSET: 44062306a36Sopenharmony_ci insn = aarch64_insn_get_str_imm_value(); 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci default: 44362306a36Sopenharmony_ci pr_err("%s: unknown load/store encoding %d\n", __func__, type); 44462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci insn = aarch64_insn_encode_ldst_size(size, insn); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 45262306a36Sopenharmony_ci base); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciu32 aarch64_insn_gen_load_literal(unsigned long pc, unsigned long addr, 45862306a36Sopenharmony_ci enum aarch64_insn_register reg, 45962306a36Sopenharmony_ci bool is64bit) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci u32 insn; 46262306a36Sopenharmony_ci long offset; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci offset = label_imm_common(pc, addr, SZ_1M); 46562306a36Sopenharmony_ci if (offset >= SZ_1M) 46662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci insn = aarch64_insn_get_ldr_lit_value(); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (is64bit) 47162306a36Sopenharmony_ci insn |= BIT(30); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, 47662306a36Sopenharmony_ci offset >> 2); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ciu32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1, 48062306a36Sopenharmony_ci enum aarch64_insn_register reg2, 48162306a36Sopenharmony_ci enum aarch64_insn_register base, 48262306a36Sopenharmony_ci int offset, 48362306a36Sopenharmony_ci enum aarch64_insn_variant variant, 48462306a36Sopenharmony_ci enum aarch64_insn_ldst_type type) 48562306a36Sopenharmony_ci{ 48662306a36Sopenharmony_ci u32 insn; 48762306a36Sopenharmony_ci int shift; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci switch (type) { 49062306a36Sopenharmony_ci case AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX: 49162306a36Sopenharmony_ci insn = aarch64_insn_get_ldp_pre_value(); 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX: 49462306a36Sopenharmony_ci insn = aarch64_insn_get_stp_pre_value(); 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci case AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX: 49762306a36Sopenharmony_ci insn = aarch64_insn_get_ldp_post_value(); 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci case AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX: 50062306a36Sopenharmony_ci insn = aarch64_insn_get_stp_post_value(); 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci default: 50362306a36Sopenharmony_ci pr_err("%s: unknown load/store encoding %d\n", __func__, type); 50462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci switch (variant) { 50862306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 50962306a36Sopenharmony_ci if ((offset & 0x3) || (offset < -256) || (offset > 252)) { 51062306a36Sopenharmony_ci pr_err("%s: offset must be multiples of 4 in the range of [-256, 252] %d\n", 51162306a36Sopenharmony_ci __func__, offset); 51262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci shift = 2; 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 51762306a36Sopenharmony_ci if ((offset & 0x7) || (offset < -512) || (offset > 504)) { 51862306a36Sopenharmony_ci pr_err("%s: offset must be multiples of 8 in the range of [-512, 504] %d\n", 51962306a36Sopenharmony_ci __func__, offset); 52062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci shift = 3; 52362306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 52462306a36Sopenharmony_ci break; 52562306a36Sopenharmony_ci default: 52662306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 52762306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, 53162306a36Sopenharmony_ci reg1); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn, 53462306a36Sopenharmony_ci reg2); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 53762306a36Sopenharmony_ci base); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_7, insn, 54062306a36Sopenharmony_ci offset >> shift); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ciu32 aarch64_insn_gen_load_store_ex(enum aarch64_insn_register reg, 54462306a36Sopenharmony_ci enum aarch64_insn_register base, 54562306a36Sopenharmony_ci enum aarch64_insn_register state, 54662306a36Sopenharmony_ci enum aarch64_insn_size_type size, 54762306a36Sopenharmony_ci enum aarch64_insn_ldst_type type) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci u32 insn; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci switch (type) { 55262306a36Sopenharmony_ci case AARCH64_INSN_LDST_LOAD_EX: 55362306a36Sopenharmony_ci case AARCH64_INSN_LDST_LOAD_ACQ_EX: 55462306a36Sopenharmony_ci insn = aarch64_insn_get_load_ex_value(); 55562306a36Sopenharmony_ci if (type == AARCH64_INSN_LDST_LOAD_ACQ_EX) 55662306a36Sopenharmony_ci insn |= BIT(15); 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci case AARCH64_INSN_LDST_STORE_EX: 55962306a36Sopenharmony_ci case AARCH64_INSN_LDST_STORE_REL_EX: 56062306a36Sopenharmony_ci insn = aarch64_insn_get_store_ex_value(); 56162306a36Sopenharmony_ci if (type == AARCH64_INSN_LDST_STORE_REL_EX) 56262306a36Sopenharmony_ci insn |= BIT(15); 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci default: 56562306a36Sopenharmony_ci pr_err("%s: unknown load/store exclusive encoding %d\n", __func__, type); 56662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci insn = aarch64_insn_encode_ldst_size(size, insn); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, 57262306a36Sopenharmony_ci reg); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 57562306a36Sopenharmony_ci base); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT2, insn, 57862306a36Sopenharmony_ci AARCH64_INSN_REG_ZR); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn, 58162306a36Sopenharmony_ci state); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci#ifdef CONFIG_ARM64_LSE_ATOMICS 58562306a36Sopenharmony_cistatic u32 aarch64_insn_encode_ldst_order(enum aarch64_insn_mem_order_type type, 58662306a36Sopenharmony_ci u32 insn) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci u32 order; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci switch (type) { 59162306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_NONE: 59262306a36Sopenharmony_ci order = 0; 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_ACQ: 59562306a36Sopenharmony_ci order = 2; 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_REL: 59862306a36Sopenharmony_ci order = 1; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_ACQREL: 60162306a36Sopenharmony_ci order = 3; 60262306a36Sopenharmony_ci break; 60362306a36Sopenharmony_ci default: 60462306a36Sopenharmony_ci pr_err("%s: unknown mem order %d\n", __func__, type); 60562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci insn &= ~GENMASK(23, 22); 60962306a36Sopenharmony_ci insn |= order << 22; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return insn; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ciu32 aarch64_insn_gen_atomic_ld_op(enum aarch64_insn_register result, 61562306a36Sopenharmony_ci enum aarch64_insn_register address, 61662306a36Sopenharmony_ci enum aarch64_insn_register value, 61762306a36Sopenharmony_ci enum aarch64_insn_size_type size, 61862306a36Sopenharmony_ci enum aarch64_insn_mem_atomic_op op, 61962306a36Sopenharmony_ci enum aarch64_insn_mem_order_type order) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci u32 insn; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci switch (op) { 62462306a36Sopenharmony_ci case AARCH64_INSN_MEM_ATOMIC_ADD: 62562306a36Sopenharmony_ci insn = aarch64_insn_get_ldadd_value(); 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci case AARCH64_INSN_MEM_ATOMIC_CLR: 62862306a36Sopenharmony_ci insn = aarch64_insn_get_ldclr_value(); 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci case AARCH64_INSN_MEM_ATOMIC_EOR: 63162306a36Sopenharmony_ci insn = aarch64_insn_get_ldeor_value(); 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci case AARCH64_INSN_MEM_ATOMIC_SET: 63462306a36Sopenharmony_ci insn = aarch64_insn_get_ldset_value(); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case AARCH64_INSN_MEM_ATOMIC_SWP: 63762306a36Sopenharmony_ci insn = aarch64_insn_get_swp_value(); 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci default: 64062306a36Sopenharmony_ci pr_err("%s: unimplemented mem atomic op %d\n", __func__, op); 64162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci switch (size) { 64562306a36Sopenharmony_ci case AARCH64_INSN_SIZE_32: 64662306a36Sopenharmony_ci case AARCH64_INSN_SIZE_64: 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci default: 64962306a36Sopenharmony_ci pr_err("%s: unimplemented size encoding %d\n", __func__, size); 65062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci insn = aarch64_insn_encode_ldst_size(size, insn); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci insn = aarch64_insn_encode_ldst_order(order, insn); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, 65862306a36Sopenharmony_ci result); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 66162306a36Sopenharmony_ci address); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn, 66462306a36Sopenharmony_ci value); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic u32 aarch64_insn_encode_cas_order(enum aarch64_insn_mem_order_type type, 66862306a36Sopenharmony_ci u32 insn) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci u32 order; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci switch (type) { 67362306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_NONE: 67462306a36Sopenharmony_ci order = 0; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_ACQ: 67762306a36Sopenharmony_ci order = BIT(22); 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_REL: 68062306a36Sopenharmony_ci order = BIT(15); 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci case AARCH64_INSN_MEM_ORDER_ACQREL: 68362306a36Sopenharmony_ci order = BIT(15) | BIT(22); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci default: 68662306a36Sopenharmony_ci pr_err("%s: unknown mem order %d\n", __func__, type); 68762306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci insn &= ~(BIT(15) | BIT(22)); 69162306a36Sopenharmony_ci insn |= order; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci return insn; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ciu32 aarch64_insn_gen_cas(enum aarch64_insn_register result, 69762306a36Sopenharmony_ci enum aarch64_insn_register address, 69862306a36Sopenharmony_ci enum aarch64_insn_register value, 69962306a36Sopenharmony_ci enum aarch64_insn_size_type size, 70062306a36Sopenharmony_ci enum aarch64_insn_mem_order_type order) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci u32 insn; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci switch (size) { 70562306a36Sopenharmony_ci case AARCH64_INSN_SIZE_32: 70662306a36Sopenharmony_ci case AARCH64_INSN_SIZE_64: 70762306a36Sopenharmony_ci break; 70862306a36Sopenharmony_ci default: 70962306a36Sopenharmony_ci pr_err("%s: unimplemented size encoding %d\n", __func__, size); 71062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci insn = aarch64_insn_get_cas_value(); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci insn = aarch64_insn_encode_ldst_size(size, insn); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci insn = aarch64_insn_encode_cas_order(order, insn); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, 72062306a36Sopenharmony_ci result); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 72362306a36Sopenharmony_ci address); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RS, insn, 72662306a36Sopenharmony_ci value); 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci#endif 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ciu32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst, 73162306a36Sopenharmony_ci enum aarch64_insn_register src, 73262306a36Sopenharmony_ci int imm, enum aarch64_insn_variant variant, 73362306a36Sopenharmony_ci enum aarch64_insn_adsb_type type) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci u32 insn; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci switch (type) { 73862306a36Sopenharmony_ci case AARCH64_INSN_ADSB_ADD: 73962306a36Sopenharmony_ci insn = aarch64_insn_get_add_imm_value(); 74062306a36Sopenharmony_ci break; 74162306a36Sopenharmony_ci case AARCH64_INSN_ADSB_SUB: 74262306a36Sopenharmony_ci insn = aarch64_insn_get_sub_imm_value(); 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci case AARCH64_INSN_ADSB_ADD_SETFLAGS: 74562306a36Sopenharmony_ci insn = aarch64_insn_get_adds_imm_value(); 74662306a36Sopenharmony_ci break; 74762306a36Sopenharmony_ci case AARCH64_INSN_ADSB_SUB_SETFLAGS: 74862306a36Sopenharmony_ci insn = aarch64_insn_get_subs_imm_value(); 74962306a36Sopenharmony_ci break; 75062306a36Sopenharmony_ci default: 75162306a36Sopenharmony_ci pr_err("%s: unknown add/sub encoding %d\n", __func__, type); 75262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci switch (variant) { 75662306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 75962306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 76062306a36Sopenharmony_ci break; 76162306a36Sopenharmony_ci default: 76262306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 76362306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* We can't encode more than a 24bit value (12bit + 12bit shift) */ 76762306a36Sopenharmony_ci if (imm & ~(BIT(24) - 1)) 76862306a36Sopenharmony_ci goto out; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci /* If we have something in the top 12 bits... */ 77162306a36Sopenharmony_ci if (imm & ~(SZ_4K - 1)) { 77262306a36Sopenharmony_ci /* ... and in the low 12 bits -> error */ 77362306a36Sopenharmony_ci if (imm & (SZ_4K - 1)) 77462306a36Sopenharmony_ci goto out; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci imm >>= 12; 77762306a36Sopenharmony_ci insn |= AARCH64_INSN_LSL_12; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ciout: 78762306a36Sopenharmony_ci pr_err("%s: invalid immediate encoding %d\n", __func__, imm); 78862306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ciu32 aarch64_insn_gen_bitfield(enum aarch64_insn_register dst, 79262306a36Sopenharmony_ci enum aarch64_insn_register src, 79362306a36Sopenharmony_ci int immr, int imms, 79462306a36Sopenharmony_ci enum aarch64_insn_variant variant, 79562306a36Sopenharmony_ci enum aarch64_insn_bitfield_type type) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci u32 insn; 79862306a36Sopenharmony_ci u32 mask; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci switch (type) { 80162306a36Sopenharmony_ci case AARCH64_INSN_BITFIELD_MOVE: 80262306a36Sopenharmony_ci insn = aarch64_insn_get_bfm_value(); 80362306a36Sopenharmony_ci break; 80462306a36Sopenharmony_ci case AARCH64_INSN_BITFIELD_MOVE_UNSIGNED: 80562306a36Sopenharmony_ci insn = aarch64_insn_get_ubfm_value(); 80662306a36Sopenharmony_ci break; 80762306a36Sopenharmony_ci case AARCH64_INSN_BITFIELD_MOVE_SIGNED: 80862306a36Sopenharmony_ci insn = aarch64_insn_get_sbfm_value(); 80962306a36Sopenharmony_ci break; 81062306a36Sopenharmony_ci default: 81162306a36Sopenharmony_ci pr_err("%s: unknown bitfield encoding %d\n", __func__, type); 81262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci switch (variant) { 81662306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 81762306a36Sopenharmony_ci mask = GENMASK(4, 0); 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 82062306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT | AARCH64_INSN_N_BIT; 82162306a36Sopenharmony_ci mask = GENMASK(5, 0); 82262306a36Sopenharmony_ci break; 82362306a36Sopenharmony_ci default: 82462306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 82562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (immr & ~mask) { 82962306a36Sopenharmony_ci pr_err("%s: invalid immr encoding %d\n", __func__, immr); 83062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci if (imms & ~mask) { 83362306a36Sopenharmony_ci pr_err("%s: invalid imms encoding %d\n", __func__, imms); 83462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms); 84462306a36Sopenharmony_ci} 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ciu32 aarch64_insn_gen_movewide(enum aarch64_insn_register dst, 84762306a36Sopenharmony_ci int imm, int shift, 84862306a36Sopenharmony_ci enum aarch64_insn_variant variant, 84962306a36Sopenharmony_ci enum aarch64_insn_movewide_type type) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci u32 insn; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci switch (type) { 85462306a36Sopenharmony_ci case AARCH64_INSN_MOVEWIDE_ZERO: 85562306a36Sopenharmony_ci insn = aarch64_insn_get_movz_value(); 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci case AARCH64_INSN_MOVEWIDE_KEEP: 85862306a36Sopenharmony_ci insn = aarch64_insn_get_movk_value(); 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci case AARCH64_INSN_MOVEWIDE_INVERSE: 86162306a36Sopenharmony_ci insn = aarch64_insn_get_movn_value(); 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci default: 86462306a36Sopenharmony_ci pr_err("%s: unknown movewide encoding %d\n", __func__, type); 86562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (imm & ~(SZ_64K - 1)) { 86962306a36Sopenharmony_ci pr_err("%s: invalid immediate encoding %d\n", __func__, imm); 87062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci switch (variant) { 87462306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 87562306a36Sopenharmony_ci if (shift != 0 && shift != 16) { 87662306a36Sopenharmony_ci pr_err("%s: invalid shift encoding %d\n", __func__, 87762306a36Sopenharmony_ci shift); 87862306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci break; 88162306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 88262306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 88362306a36Sopenharmony_ci if (shift != 0 && shift != 16 && shift != 32 && shift != 48) { 88462306a36Sopenharmony_ci pr_err("%s: invalid shift encoding %d\n", __func__, 88562306a36Sopenharmony_ci shift); 88662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci default: 89062306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 89162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci insn |= (shift >> 4) << 21; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm); 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ciu32 aarch64_insn_gen_add_sub_shifted_reg(enum aarch64_insn_register dst, 90262306a36Sopenharmony_ci enum aarch64_insn_register src, 90362306a36Sopenharmony_ci enum aarch64_insn_register reg, 90462306a36Sopenharmony_ci int shift, 90562306a36Sopenharmony_ci enum aarch64_insn_variant variant, 90662306a36Sopenharmony_ci enum aarch64_insn_adsb_type type) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci u32 insn; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci switch (type) { 91162306a36Sopenharmony_ci case AARCH64_INSN_ADSB_ADD: 91262306a36Sopenharmony_ci insn = aarch64_insn_get_add_value(); 91362306a36Sopenharmony_ci break; 91462306a36Sopenharmony_ci case AARCH64_INSN_ADSB_SUB: 91562306a36Sopenharmony_ci insn = aarch64_insn_get_sub_value(); 91662306a36Sopenharmony_ci break; 91762306a36Sopenharmony_ci case AARCH64_INSN_ADSB_ADD_SETFLAGS: 91862306a36Sopenharmony_ci insn = aarch64_insn_get_adds_value(); 91962306a36Sopenharmony_ci break; 92062306a36Sopenharmony_ci case AARCH64_INSN_ADSB_SUB_SETFLAGS: 92162306a36Sopenharmony_ci insn = aarch64_insn_get_subs_value(); 92262306a36Sopenharmony_ci break; 92362306a36Sopenharmony_ci default: 92462306a36Sopenharmony_ci pr_err("%s: unknown add/sub encoding %d\n", __func__, type); 92562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci switch (variant) { 92962306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 93062306a36Sopenharmony_ci if (shift & ~(SZ_32 - 1)) { 93162306a36Sopenharmony_ci pr_err("%s: invalid shift encoding %d\n", __func__, 93262306a36Sopenharmony_ci shift); 93362306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 93762306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 93862306a36Sopenharmony_ci if (shift & ~(SZ_64 - 1)) { 93962306a36Sopenharmony_ci pr_err("%s: invalid shift encoding %d\n", __func__, 94062306a36Sopenharmony_ci shift); 94162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci default: 94562306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 94662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ciu32 aarch64_insn_gen_data1(enum aarch64_insn_register dst, 96062306a36Sopenharmony_ci enum aarch64_insn_register src, 96162306a36Sopenharmony_ci enum aarch64_insn_variant variant, 96262306a36Sopenharmony_ci enum aarch64_insn_data1_type type) 96362306a36Sopenharmony_ci{ 96462306a36Sopenharmony_ci u32 insn; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci switch (type) { 96762306a36Sopenharmony_ci case AARCH64_INSN_DATA1_REVERSE_16: 96862306a36Sopenharmony_ci insn = aarch64_insn_get_rev16_value(); 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci case AARCH64_INSN_DATA1_REVERSE_32: 97162306a36Sopenharmony_ci insn = aarch64_insn_get_rev32_value(); 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci case AARCH64_INSN_DATA1_REVERSE_64: 97462306a36Sopenharmony_ci if (variant != AARCH64_INSN_VARIANT_64BIT) { 97562306a36Sopenharmony_ci pr_err("%s: invalid variant for reverse64 %d\n", 97662306a36Sopenharmony_ci __func__, variant); 97762306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci insn = aarch64_insn_get_rev64_value(); 98062306a36Sopenharmony_ci break; 98162306a36Sopenharmony_ci default: 98262306a36Sopenharmony_ci pr_err("%s: unknown data1 encoding %d\n", __func__, type); 98362306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci switch (variant) { 98762306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 98862306a36Sopenharmony_ci break; 98962306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 99062306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci default: 99362306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 99462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ciu32 aarch64_insn_gen_data2(enum aarch64_insn_register dst, 100362306a36Sopenharmony_ci enum aarch64_insn_register src, 100462306a36Sopenharmony_ci enum aarch64_insn_register reg, 100562306a36Sopenharmony_ci enum aarch64_insn_variant variant, 100662306a36Sopenharmony_ci enum aarch64_insn_data2_type type) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci u32 insn; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci switch (type) { 101162306a36Sopenharmony_ci case AARCH64_INSN_DATA2_UDIV: 101262306a36Sopenharmony_ci insn = aarch64_insn_get_udiv_value(); 101362306a36Sopenharmony_ci break; 101462306a36Sopenharmony_ci case AARCH64_INSN_DATA2_SDIV: 101562306a36Sopenharmony_ci insn = aarch64_insn_get_sdiv_value(); 101662306a36Sopenharmony_ci break; 101762306a36Sopenharmony_ci case AARCH64_INSN_DATA2_LSLV: 101862306a36Sopenharmony_ci insn = aarch64_insn_get_lslv_value(); 101962306a36Sopenharmony_ci break; 102062306a36Sopenharmony_ci case AARCH64_INSN_DATA2_LSRV: 102162306a36Sopenharmony_ci insn = aarch64_insn_get_lsrv_value(); 102262306a36Sopenharmony_ci break; 102362306a36Sopenharmony_ci case AARCH64_INSN_DATA2_ASRV: 102462306a36Sopenharmony_ci insn = aarch64_insn_get_asrv_value(); 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci case AARCH64_INSN_DATA2_RORV: 102762306a36Sopenharmony_ci insn = aarch64_insn_get_rorv_value(); 102862306a36Sopenharmony_ci break; 102962306a36Sopenharmony_ci default: 103062306a36Sopenharmony_ci pr_err("%s: unknown data2 encoding %d\n", __func__, type); 103162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci switch (variant) { 103562306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 103662306a36Sopenharmony_ci break; 103762306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 103862306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 103962306a36Sopenharmony_ci break; 104062306a36Sopenharmony_ci default: 104162306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 104262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg); 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ciu32 aarch64_insn_gen_data3(enum aarch64_insn_register dst, 105362306a36Sopenharmony_ci enum aarch64_insn_register src, 105462306a36Sopenharmony_ci enum aarch64_insn_register reg1, 105562306a36Sopenharmony_ci enum aarch64_insn_register reg2, 105662306a36Sopenharmony_ci enum aarch64_insn_variant variant, 105762306a36Sopenharmony_ci enum aarch64_insn_data3_type type) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci u32 insn; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci switch (type) { 106262306a36Sopenharmony_ci case AARCH64_INSN_DATA3_MADD: 106362306a36Sopenharmony_ci insn = aarch64_insn_get_madd_value(); 106462306a36Sopenharmony_ci break; 106562306a36Sopenharmony_ci case AARCH64_INSN_DATA3_MSUB: 106662306a36Sopenharmony_ci insn = aarch64_insn_get_msub_value(); 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci default: 106962306a36Sopenharmony_ci pr_err("%s: unknown data3 encoding %d\n", __func__, type); 107062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci switch (variant) { 107462306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 107762306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci default: 108062306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 108162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RA, insn, src); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, 108962306a36Sopenharmony_ci reg1); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, 109262306a36Sopenharmony_ci reg2); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ciu32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst, 109662306a36Sopenharmony_ci enum aarch64_insn_register src, 109762306a36Sopenharmony_ci enum aarch64_insn_register reg, 109862306a36Sopenharmony_ci int shift, 109962306a36Sopenharmony_ci enum aarch64_insn_variant variant, 110062306a36Sopenharmony_ci enum aarch64_insn_logic_type type) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci u32 insn; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci switch (type) { 110562306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_AND: 110662306a36Sopenharmony_ci insn = aarch64_insn_get_and_value(); 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_BIC: 110962306a36Sopenharmony_ci insn = aarch64_insn_get_bic_value(); 111062306a36Sopenharmony_ci break; 111162306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_ORR: 111262306a36Sopenharmony_ci insn = aarch64_insn_get_orr_value(); 111362306a36Sopenharmony_ci break; 111462306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_ORN: 111562306a36Sopenharmony_ci insn = aarch64_insn_get_orn_value(); 111662306a36Sopenharmony_ci break; 111762306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_EOR: 111862306a36Sopenharmony_ci insn = aarch64_insn_get_eor_value(); 111962306a36Sopenharmony_ci break; 112062306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_EON: 112162306a36Sopenharmony_ci insn = aarch64_insn_get_eon_value(); 112262306a36Sopenharmony_ci break; 112362306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_AND_SETFLAGS: 112462306a36Sopenharmony_ci insn = aarch64_insn_get_ands_value(); 112562306a36Sopenharmony_ci break; 112662306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_BIC_SETFLAGS: 112762306a36Sopenharmony_ci insn = aarch64_insn_get_bics_value(); 112862306a36Sopenharmony_ci break; 112962306a36Sopenharmony_ci default: 113062306a36Sopenharmony_ci pr_err("%s: unknown logical encoding %d\n", __func__, type); 113162306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 113262306a36Sopenharmony_ci } 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci switch (variant) { 113562306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 113662306a36Sopenharmony_ci if (shift & ~(SZ_32 - 1)) { 113762306a36Sopenharmony_ci pr_err("%s: invalid shift encoding %d\n", __func__, 113862306a36Sopenharmony_ci shift); 113962306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 114062306a36Sopenharmony_ci } 114162306a36Sopenharmony_ci break; 114262306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 114362306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 114462306a36Sopenharmony_ci if (shift & ~(SZ_64 - 1)) { 114562306a36Sopenharmony_ci pr_err("%s: invalid shift encoding %d\n", __func__, 114662306a36Sopenharmony_ci shift); 114762306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci break; 115062306a36Sopenharmony_ci default: 115162306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 115262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, dst); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, src); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, reg); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift); 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci/* 116662306a36Sopenharmony_ci * MOV (register) is architecturally an alias of ORR (shifted register) where 116762306a36Sopenharmony_ci * MOV <*d>, <*m> is equivalent to ORR <*d>, <*ZR>, <*m> 116862306a36Sopenharmony_ci */ 116962306a36Sopenharmony_ciu32 aarch64_insn_gen_move_reg(enum aarch64_insn_register dst, 117062306a36Sopenharmony_ci enum aarch64_insn_register src, 117162306a36Sopenharmony_ci enum aarch64_insn_variant variant) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci return aarch64_insn_gen_logical_shifted_reg(dst, AARCH64_INSN_REG_ZR, 117462306a36Sopenharmony_ci src, 0, variant, 117562306a36Sopenharmony_ci AARCH64_INSN_LOGIC_ORR); 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ciu32 aarch64_insn_gen_adr(unsigned long pc, unsigned long addr, 117962306a36Sopenharmony_ci enum aarch64_insn_register reg, 118062306a36Sopenharmony_ci enum aarch64_insn_adr_type type) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci u32 insn; 118362306a36Sopenharmony_ci s32 offset; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci switch (type) { 118662306a36Sopenharmony_ci case AARCH64_INSN_ADR_TYPE_ADR: 118762306a36Sopenharmony_ci insn = aarch64_insn_get_adr_value(); 118862306a36Sopenharmony_ci offset = addr - pc; 118962306a36Sopenharmony_ci break; 119062306a36Sopenharmony_ci case AARCH64_INSN_ADR_TYPE_ADRP: 119162306a36Sopenharmony_ci insn = aarch64_insn_get_adrp_value(); 119262306a36Sopenharmony_ci offset = (addr - ALIGN_DOWN(pc, SZ_4K)) >> 12; 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci default: 119562306a36Sopenharmony_ci pr_err("%s: unknown adr encoding %d\n", __func__, type); 119662306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (offset < -SZ_1M || offset >= SZ_1M) 120062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, reg); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_ADR, insn, offset); 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci/* 120862306a36Sopenharmony_ci * Decode the imm field of a branch, and return the byte offset as a 120962306a36Sopenharmony_ci * signed value (so it can be used when computing a new branch 121062306a36Sopenharmony_ci * target). 121162306a36Sopenharmony_ci */ 121262306a36Sopenharmony_cis32 aarch64_get_branch_offset(u32 insn) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci s32 imm; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) { 121762306a36Sopenharmony_ci imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn); 121862306a36Sopenharmony_ci return (imm << 6) >> 4; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) || 122262306a36Sopenharmony_ci aarch64_insn_is_bcond(insn)) { 122362306a36Sopenharmony_ci imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_19, insn); 122462306a36Sopenharmony_ci return (imm << 13) >> 11; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) { 122862306a36Sopenharmony_ci imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_14, insn); 122962306a36Sopenharmony_ci return (imm << 18) >> 16; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* Unhandled instruction */ 123362306a36Sopenharmony_ci BUG(); 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci/* 123762306a36Sopenharmony_ci * Encode the displacement of a branch in the imm field and return the 123862306a36Sopenharmony_ci * updated instruction. 123962306a36Sopenharmony_ci */ 124062306a36Sopenharmony_ciu32 aarch64_set_branch_offset(u32 insn, s32 offset) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) 124362306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn, 124462306a36Sopenharmony_ci offset >> 2); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) || 124762306a36Sopenharmony_ci aarch64_insn_is_bcond(insn)) 124862306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn, 124962306a36Sopenharmony_ci offset >> 2); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) 125262306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_14, insn, 125362306a36Sopenharmony_ci offset >> 2); 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* Unhandled instruction */ 125662306a36Sopenharmony_ci BUG(); 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cis32 aarch64_insn_adrp_get_offset(u32 insn) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci BUG_ON(!aarch64_insn_is_adrp(insn)); 126262306a36Sopenharmony_ci return aarch64_insn_decode_immediate(AARCH64_INSN_IMM_ADR, insn) << 12; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ciu32 aarch64_insn_adrp_set_offset(u32 insn, s32 offset) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci BUG_ON(!aarch64_insn_is_adrp(insn)); 126862306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_ADR, insn, 126962306a36Sopenharmony_ci offset >> 12); 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci/* 127362306a36Sopenharmony_ci * Extract the Op/CR data from a msr/mrs instruction. 127462306a36Sopenharmony_ci */ 127562306a36Sopenharmony_ciu32 aarch64_insn_extract_system_reg(u32 insn) 127662306a36Sopenharmony_ci{ 127762306a36Sopenharmony_ci return (insn & 0x1FFFE0) >> 5; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_cibool aarch32_insn_is_wide(u32 insn) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci return insn >= 0xe800; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci/* 128662306a36Sopenharmony_ci * Macros/defines for extracting register numbers from instruction. 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_ciu32 aarch32_insn_extract_reg_num(u32 insn, int offset) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci return (insn & (0xf << offset)) >> offset; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci#define OPC2_MASK 0x7 129462306a36Sopenharmony_ci#define OPC2_OFFSET 5 129562306a36Sopenharmony_ciu32 aarch32_insn_mcr_extract_opc2(u32 insn) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET; 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci#define CRM_MASK 0xf 130162306a36Sopenharmony_ciu32 aarch32_insn_mcr_extract_crm(u32 insn) 130262306a36Sopenharmony_ci{ 130362306a36Sopenharmony_ci return insn & CRM_MASK; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic bool range_of_ones(u64 val) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci /* Doesn't handle full ones or full zeroes */ 130962306a36Sopenharmony_ci u64 sval = val >> __ffs64(val); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci /* One of Sean Eron Anderson's bithack tricks */ 131262306a36Sopenharmony_ci return ((sval + 1) & (sval)) == 0; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_cistatic u32 aarch64_encode_immediate(u64 imm, 131662306a36Sopenharmony_ci enum aarch64_insn_variant variant, 131762306a36Sopenharmony_ci u32 insn) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci unsigned int immr, imms, n, ones, ror, esz, tmp; 132062306a36Sopenharmony_ci u64 mask; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci switch (variant) { 132362306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 132462306a36Sopenharmony_ci esz = 32; 132562306a36Sopenharmony_ci break; 132662306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 132762306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 132862306a36Sopenharmony_ci esz = 64; 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci default: 133162306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 133262306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci mask = GENMASK(esz - 1, 0); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci /* Can't encode full zeroes, full ones, or value wider than the mask */ 133862306a36Sopenharmony_ci if (!imm || imm == mask || imm & ~mask) 133962306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /* 134262306a36Sopenharmony_ci * Inverse of Replicate(). Try to spot a repeating pattern 134362306a36Sopenharmony_ci * with a pow2 stride. 134462306a36Sopenharmony_ci */ 134562306a36Sopenharmony_ci for (tmp = esz / 2; tmp >= 2; tmp /= 2) { 134662306a36Sopenharmony_ci u64 emask = BIT(tmp) - 1; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci if ((imm & emask) != ((imm >> tmp) & emask)) 134962306a36Sopenharmony_ci break; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci esz = tmp; 135262306a36Sopenharmony_ci mask = emask; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* N is only set if we're encoding a 64bit value */ 135662306a36Sopenharmony_ci n = esz == 64; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* Trim imm to the element size */ 135962306a36Sopenharmony_ci imm &= mask; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci /* That's how many ones we need to encode */ 136262306a36Sopenharmony_ci ones = hweight64(imm); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* 136562306a36Sopenharmony_ci * imms is set to (ones - 1), prefixed with a string of ones 136662306a36Sopenharmony_ci * and a zero if they fit. Cap it to 6 bits. 136762306a36Sopenharmony_ci */ 136862306a36Sopenharmony_ci imms = ones - 1; 136962306a36Sopenharmony_ci imms |= 0xf << ffs(esz); 137062306a36Sopenharmony_ci imms &= BIT(6) - 1; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci /* Compute the rotation */ 137362306a36Sopenharmony_ci if (range_of_ones(imm)) { 137462306a36Sopenharmony_ci /* 137562306a36Sopenharmony_ci * Pattern: 0..01..10..0 137662306a36Sopenharmony_ci * 137762306a36Sopenharmony_ci * Compute how many rotate we need to align it right 137862306a36Sopenharmony_ci */ 137962306a36Sopenharmony_ci ror = __ffs64(imm); 138062306a36Sopenharmony_ci } else { 138162306a36Sopenharmony_ci /* 138262306a36Sopenharmony_ci * Pattern: 0..01..10..01..1 138362306a36Sopenharmony_ci * 138462306a36Sopenharmony_ci * Fill the unused top bits with ones, and check if 138562306a36Sopenharmony_ci * the result is a valid immediate (all ones with a 138662306a36Sopenharmony_ci * contiguous ranges of zeroes). 138762306a36Sopenharmony_ci */ 138862306a36Sopenharmony_ci imm |= ~mask; 138962306a36Sopenharmony_ci if (!range_of_ones(~imm)) 139062306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci /* 139362306a36Sopenharmony_ci * Compute the rotation to get a continuous set of 139462306a36Sopenharmony_ci * ones, with the first bit set at position 0 139562306a36Sopenharmony_ci */ 139662306a36Sopenharmony_ci ror = fls64(~imm); 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci /* 140062306a36Sopenharmony_ci * immr is the number of bits we need to rotate back to the 140162306a36Sopenharmony_ci * original set of ones. Note that this is relative to the 140262306a36Sopenharmony_ci * element size... 140362306a36Sopenharmony_ci */ 140462306a36Sopenharmony_ci immr = (esz - ror) % esz; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, n); 140762306a36Sopenharmony_ci insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_R, insn, immr); 140862306a36Sopenharmony_ci return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, imms); 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ciu32 aarch64_insn_gen_logical_immediate(enum aarch64_insn_logic_type type, 141262306a36Sopenharmony_ci enum aarch64_insn_variant variant, 141362306a36Sopenharmony_ci enum aarch64_insn_register Rn, 141462306a36Sopenharmony_ci enum aarch64_insn_register Rd, 141562306a36Sopenharmony_ci u64 imm) 141662306a36Sopenharmony_ci{ 141762306a36Sopenharmony_ci u32 insn; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci switch (type) { 142062306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_AND: 142162306a36Sopenharmony_ci insn = aarch64_insn_get_and_imm_value(); 142262306a36Sopenharmony_ci break; 142362306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_ORR: 142462306a36Sopenharmony_ci insn = aarch64_insn_get_orr_imm_value(); 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_EOR: 142762306a36Sopenharmony_ci insn = aarch64_insn_get_eor_imm_value(); 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci case AARCH64_INSN_LOGIC_AND_SETFLAGS: 143062306a36Sopenharmony_ci insn = aarch64_insn_get_ands_imm_value(); 143162306a36Sopenharmony_ci break; 143262306a36Sopenharmony_ci default: 143362306a36Sopenharmony_ci pr_err("%s: unknown logical encoding %d\n", __func__, type); 143462306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd); 143862306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn); 143962306a36Sopenharmony_ci return aarch64_encode_immediate(imm, variant, insn); 144062306a36Sopenharmony_ci} 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ciu32 aarch64_insn_gen_extr(enum aarch64_insn_variant variant, 144362306a36Sopenharmony_ci enum aarch64_insn_register Rm, 144462306a36Sopenharmony_ci enum aarch64_insn_register Rn, 144562306a36Sopenharmony_ci enum aarch64_insn_register Rd, 144662306a36Sopenharmony_ci u8 lsb) 144762306a36Sopenharmony_ci{ 144862306a36Sopenharmony_ci u32 insn; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci insn = aarch64_insn_get_extr_value(); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci switch (variant) { 145362306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_32BIT: 145462306a36Sopenharmony_ci if (lsb > 31) 145562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 145662306a36Sopenharmony_ci break; 145762306a36Sopenharmony_ci case AARCH64_INSN_VARIANT_64BIT: 145862306a36Sopenharmony_ci if (lsb > 63) 145962306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 146062306a36Sopenharmony_ci insn |= AARCH64_INSN_SF_BIT; 146162306a36Sopenharmony_ci insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_N, insn, 1); 146262306a36Sopenharmony_ci break; 146362306a36Sopenharmony_ci default: 146462306a36Sopenharmony_ci pr_err("%s: unknown variant encoding %d\n", __func__, variant); 146562306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_S, insn, lsb); 146962306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, Rd); 147062306a36Sopenharmony_ci insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn, Rn); 147162306a36Sopenharmony_ci return aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RM, insn, Rm); 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ciu32 aarch64_insn_gen_dmb(enum aarch64_insn_mb_type type) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci u32 opt; 147762306a36Sopenharmony_ci u32 insn; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci switch (type) { 148062306a36Sopenharmony_ci case AARCH64_INSN_MB_SY: 148162306a36Sopenharmony_ci opt = 0xf; 148262306a36Sopenharmony_ci break; 148362306a36Sopenharmony_ci case AARCH64_INSN_MB_ST: 148462306a36Sopenharmony_ci opt = 0xe; 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci case AARCH64_INSN_MB_LD: 148762306a36Sopenharmony_ci opt = 0xd; 148862306a36Sopenharmony_ci break; 148962306a36Sopenharmony_ci case AARCH64_INSN_MB_ISH: 149062306a36Sopenharmony_ci opt = 0xb; 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci case AARCH64_INSN_MB_ISHST: 149362306a36Sopenharmony_ci opt = 0xa; 149462306a36Sopenharmony_ci break; 149562306a36Sopenharmony_ci case AARCH64_INSN_MB_ISHLD: 149662306a36Sopenharmony_ci opt = 0x9; 149762306a36Sopenharmony_ci break; 149862306a36Sopenharmony_ci case AARCH64_INSN_MB_NSH: 149962306a36Sopenharmony_ci opt = 0x7; 150062306a36Sopenharmony_ci break; 150162306a36Sopenharmony_ci case AARCH64_INSN_MB_NSHST: 150262306a36Sopenharmony_ci opt = 0x6; 150362306a36Sopenharmony_ci break; 150462306a36Sopenharmony_ci case AARCH64_INSN_MB_NSHLD: 150562306a36Sopenharmony_ci opt = 0x5; 150662306a36Sopenharmony_ci break; 150762306a36Sopenharmony_ci default: 150862306a36Sopenharmony_ci pr_err("%s: unknown dmb type %d\n", __func__, type); 150962306a36Sopenharmony_ci return AARCH64_BREAK_FAULT; 151062306a36Sopenharmony_ci } 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci insn = aarch64_insn_get_dmb_value(); 151362306a36Sopenharmony_ci insn &= ~GENMASK(11, 8); 151462306a36Sopenharmony_ci insn |= (opt << 8); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci return insn; 151762306a36Sopenharmony_ci} 1518