diff mbox

[v6,4/6] arm64: insn: add instruction decoders for ldp/stp and add/sub

Message ID 1447828989-4980-5-git-send-email-takahiro.akashi@linaro.org
State New
Headers show

Commit Message

AKASHI Takahiro Nov. 18, 2015, 6:43 a.m. UTC
A function prologue analyzer is a requisite for implementing stack tracer
and getting better views of stack usages on arm64.
To implement a function prologue analyzer, we have to be able to decode,
at least, stp, add, sub and mov instructions.

This patch adds decoders for those instructions, that are used solely
by stack tracer for now, but generic enough for other uses.

Reviewed-by: Jungseok Lee <jungseoklee85@gmail.com>

Tested-by: Jungseok Lee <jungseoklee85@gmail.com>

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

---
 arch/arm64/include/asm/insn.h |   18 ++++++++
 arch/arm64/kernel/insn.c      |  102 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 120 insertions(+)

-- 
1.7.9.5


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

Comments

Will Deacon Dec. 8, 2015, 6:15 p.m. UTC | #1
On Wed, Nov 18, 2015 at 03:43:07PM +0900, AKASHI Takahiro wrote:
> A function prologue analyzer is a requisite for implementing stack tracer

> and getting better views of stack usages on arm64.

> To implement a function prologue analyzer, we have to be able to decode,

> at least, stp, add, sub and mov instructions.

> 

> This patch adds decoders for those instructions, that are used solely

> by stack tracer for now, but generic enough for other uses.

> 

> Reviewed-by: Jungseok Lee <jungseoklee85@gmail.com>

> Tested-by: Jungseok Lee <jungseoklee85@gmail.com>

> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

> ---

>  arch/arm64/include/asm/insn.h |   18 ++++++++

>  arch/arm64/kernel/insn.c      |  102 +++++++++++++++++++++++++++++++++++++++++

>  2 files changed, 120 insertions(+)

> 

> diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h

> index 30e50eb..8d5c538 100644

> --- a/arch/arm64/include/asm/insn.h

> +++ b/arch/arm64/include/asm/insn.h

> @@ -165,6 +165,8 @@ enum aarch64_insn_ldst_type {

>  	AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX,

>  	AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX,

>  	AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX,

> +	AARCH64_INSN_LDST_LOAD_PAIR,

> +	AARCH64_INSN_LDST_STORE_PAIR,


For consistency with the rest of this header, we should be calling these
AARCH64_INSN_LDST_{LOAD,STORE}_PAIR_REG_OFFSET...

>  };

>  

>  enum aarch64_insn_adsb_type {

> @@ -225,6 +227,8 @@ static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \

>  

>  __AARCH64_INSN_FUNCS(str_reg,	0x3FE0EC00, 0x38206800)

>  __AARCH64_INSN_FUNCS(ldr_reg,	0x3FE0EC00, 0x38606800)

> +__AARCH64_INSN_FUNCS(stp,	0x7FC00000, 0x29000000)

> +__AARCH64_INSN_FUNCS(ldp,	0x7FC00000, 0x29400000)


... and using stp_reg/ldp_reg here.

>  __AARCH64_INSN_FUNCS(stp_post,	0x7FC00000, 0x28800000)

>  __AARCH64_INSN_FUNCS(ldp_post,	0x7FC00000, 0x28C00000)

>  __AARCH64_INSN_FUNCS(stp_pre,	0x7FC00000, 0x29800000)

> @@ -277,6 +281,7 @@ __AARCH64_INSN_FUNCS(hint,	0xFFFFF01F, 0xD503201F)

>  __AARCH64_INSN_FUNCS(br,	0xFFFFFC1F, 0xD61F0000)

>  __AARCH64_INSN_FUNCS(blr,	0xFFFFFC1F, 0xD63F0000)

>  __AARCH64_INSN_FUNCS(ret,	0xFFFFFC1F, 0xD65F0000)

> +__AARCH64_INSN_FUNCS(eret,	0xFFFFFFFF, 0xD69F00E0)


Is this encoding correct? I ended up with 0xd69f03e0.

>  #undef	__AARCH64_INSN_FUNCS

>  

> @@ -370,6 +375,19 @@ bool aarch32_insn_is_wide(u32 insn);

>  u32 aarch32_insn_extract_reg_num(u32 insn, int offset);

>  u32 aarch32_insn_mcr_extract_opc2(u32 insn);

>  u32 aarch32_insn_mcr_extract_crm(u32 insn);

> +int aarch64_insn_decode_add_sub_imm(u32 insn,

> +				    enum aarch64_insn_register *dst,

> +				    enum aarch64_insn_register *src,

> +				    int *imm,

> +				    enum aarch64_insn_variant *variant,

> +				    enum aarch64_insn_adsb_type *type);

> +int aarch64_insn_decode_load_store_pair(u32 insn,

> +					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);

>  #endif /* __ASSEMBLY__ */

>  

>  #endif	/* __ASM_INSN_H */

> diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c

> index c08b9ad..b56a66c 100644

> --- a/arch/arm64/kernel/insn.c

> +++ b/arch/arm64/kernel/insn.c

> @@ -33,6 +33,7 @@

>  #include <asm/insn.h>

>  

>  #define AARCH64_INSN_SF_BIT	BIT(31)

> +#define AARCH64_INSN_S_BIT	BIT(29)

>  #define AARCH64_INSN_N_BIT	BIT(22)

>  

>  static int aarch64_insn_encoding_class[] = {

> @@ -1141,3 +1142,104 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn)

>  {

>  	return insn & CRM_MASK;

>  }

> +

> +enum aarch64_insn_register aarch64_insn_extract_reg_num(u32 insn,

> +				enum aarch64_insn_register_type type)


Maybe call this aarch64_insn_decode_register? You're basically building
decoders for some of the *_encode_* functions we already have.

> +{

> +	int shift;

> +

> +	switch (type) {

> +	case AARCH64_INSN_REGTYPE_RT:

> +	case AARCH64_INSN_REGTYPE_RD:

> +		shift = 0;

> +		break;

> +	case AARCH64_INSN_REGTYPE_RN:

> +		shift = 5;

> +		break;

> +	case AARCH64_INSN_REGTYPE_RT2:

> +	case AARCH64_INSN_REGTYPE_RA:

> +		shift = 10;

> +		break;

> +	case AARCH64_INSN_REGTYPE_RM:

> +		shift = 16;

> +		break;

> +	default:

> +		pr_err("%s: unknown register type decoding %d\n", __func__,

> +		       type);

> +		return ~0L;

> +	}


This is largely a copy-paste from aarch64_insn_encode_register. Can you
either work with what we have or factor out the common parts, please?

> +	return (insn & (GENMASK(4, 0) << shift)) >> shift;

> +}

> +

> +int aarch64_insn_decode_add_sub_imm(u32 insn,


This one could be aarch64_insn_extract_add_sub_imm, if you like.
Then extract is the converse of gen and decode is the converse of encode.

> +				    enum aarch64_insn_register *dst,

> +				    enum aarch64_insn_register *src,

> +				    int *imm,

> +				    enum aarch64_insn_variant *variant,

> +				    enum aarch64_insn_adsb_type *type)

> +{

> +	if (aarch64_insn_is_add_imm(insn))

> +		*type =	((insn) & AARCH64_INSN_S_BIT) ?

> +				AARCH64_INSN_ADSB_ADD_SETFLAGS :

> +				AARCH64_INSN_ADSB_ADD;

> +	else if (aarch64_insn_is_sub_imm(insn))

> +		*type =	((insn) & AARCH64_INSN_S_BIT) ?

> +				AARCH64_INSN_ADSB_SUB_SETFLAGS :

> +				AARCH64_INSN_ADSB_SUB;

> +	else

> +		return 0;


Why do we need to return 0 on error? -EINVAL might make more sense.

> +

> +	*variant = (insn & AARCH64_INSN_SF_BIT) ? AARCH64_INSN_VARIANT_64BIT :

> +					AARCH64_INSN_VARIANT_32BIT;

> +

> +	*dst = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RD);

> +

> +	*src = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RN);

> +

> +	/* TODO: ignore shilft field[23:22] */


I don't understand the TODO.

> +	*imm = (int)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_12, insn);


We use u64 for imm everywhere else.

> +	return 1;

> +}


Similarly here, you're trying to implement the converse of
aarch64_insn_gen_add_sub_imm

> +

> +int aarch64_insn_decode_load_store_pair(u32 insn,

> +					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)

> +{

> +	int imm;

> +

> +	if (aarch64_insn_is_stp(insn))

> +		*type = AARCH64_INSN_LDST_STORE_PAIR;

> +	else if (aarch64_insn_is_stp_post(insn))

> +		*type = AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX;

> +	else if (aarch64_insn_is_stp_pre(insn))

> +		*type = AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX;

> +	else if (aarch64_insn_is_ldp(insn))

> +		*type = AARCH64_INSN_LDST_LOAD_PAIR;

> +	else if (aarch64_insn_is_ldp_post(insn))

> +		*type = AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX;

> +	else if (aarch64_insn_is_ldp_pre(insn))

> +		*type = AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX;

> +	else

> +		return 0;

> +

> +	*variant = (insn & AARCH64_INSN_S_BIT) ? AARCH64_INSN_VARIANT_64BIT :

> +				AARCH64_INSN_VARIANT_32BIT;

> +

> +	*reg1 = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RT);

> +

> +	*reg2 = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RT2);

> +

> +	*base = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RN);

> +

> +	imm = (int)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_7, insn);

> +	asm("sbfm %0, %0, 0, 6" : "+r" (imm));


What? Can't you write this in C?

Will

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
AKASHI Takahiro Dec. 10, 2015, 7:10 a.m. UTC | #2
Will,

I was back from my vacation.

On 12/09/2015 03:15 AM, Will Deacon wrote:
> On Wed, Nov 18, 2015 at 03:43:07PM +0900, AKASHI Takahiro wrote:

>> A function prologue analyzer is a requisite for implementing stack tracer

>> and getting better views of stack usages on arm64.

>> To implement a function prologue analyzer, we have to be able to decode,

>> at least, stp, add, sub and mov instructions.

>>

>> This patch adds decoders for those instructions, that are used solely

>> by stack tracer for now, but generic enough for other uses.

>>

>> Reviewed-by: Jungseok Lee <jungseoklee85@gmail.com>

>> Tested-by: Jungseok Lee <jungseoklee85@gmail.com>

>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

>> ---

>>   arch/arm64/include/asm/insn.h |   18 ++++++++

>>   arch/arm64/kernel/insn.c      |  102 +++++++++++++++++++++++++++++++++++++++++

>>   2 files changed, 120 insertions(+)

>>

>> diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h

>> index 30e50eb..8d5c538 100644

>> --- a/arch/arm64/include/asm/insn.h

>> +++ b/arch/arm64/include/asm/insn.h

>> @@ -165,6 +165,8 @@ enum aarch64_insn_ldst_type {

>>   	AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX,

>>   	AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX,

>>   	AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX,

>> +	AARCH64_INSN_LDST_LOAD_PAIR,

>> +	AARCH64_INSN_LDST_STORE_PAIR,

>

> For consistency with the rest of this header, we should be calling these

> AARCH64_INSN_LDST_{LOAD,STORE}_PAIR_REG_OFFSET...


OK.
(But taking arguments of registers and offset is also common to _PRE_INDEX and _POST_INDEX cases).

>>   };

>>

>>   enum aarch64_insn_adsb_type {

>> @@ -225,6 +227,8 @@ static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \

>>

>>   __AARCH64_INSN_FUNCS(str_reg,	0x3FE0EC00, 0x38206800)

>>   __AARCH64_INSN_FUNCS(ldr_reg,	0x3FE0EC00, 0x38606800)

>> +__AARCH64_INSN_FUNCS(stp,	0x7FC00000, 0x29000000)

>> +__AARCH64_INSN_FUNCS(ldp,	0x7FC00000, 0x29400000)

>

> ... and using stp_reg/ldp_reg here.


Ditto.

>>   __AARCH64_INSN_FUNCS(stp_post,	0x7FC00000, 0x28800000)

>>   __AARCH64_INSN_FUNCS(ldp_post,	0x7FC00000, 0x28C00000)

>>   __AARCH64_INSN_FUNCS(stp_pre,	0x7FC00000, 0x29800000)

>> @@ -277,6 +281,7 @@ __AARCH64_INSN_FUNCS(hint,	0xFFFFF01F, 0xD503201F)

>>   __AARCH64_INSN_FUNCS(br,	0xFFFFFC1F, 0xD61F0000)

>>   __AARCH64_INSN_FUNCS(blr,	0xFFFFFC1F, 0xD63F0000)

>>   __AARCH64_INSN_FUNCS(ret,	0xFFFFFC1F, 0xD65F0000)

>> +__AARCH64_INSN_FUNCS(eret,	0xFFFFFFFF, 0xD69F00E0)

>

> Is this encoding correct? I ended up with 0xd69f03e0.


Fixed it in v6.

>>   #undef	__AARCH64_INSN_FUNCS

>>

>> @@ -370,6 +375,19 @@ bool aarch32_insn_is_wide(u32 insn);

>>   u32 aarch32_insn_extract_reg_num(u32 insn, int offset);

>>   u32 aarch32_insn_mcr_extract_opc2(u32 insn);

>>   u32 aarch32_insn_mcr_extract_crm(u32 insn);

>> +int aarch64_insn_decode_add_sub_imm(u32 insn,

>> +				    enum aarch64_insn_register *dst,

>> +				    enum aarch64_insn_register *src,

>> +				    int *imm,

>> +				    enum aarch64_insn_variant *variant,

>> +				    enum aarch64_insn_adsb_type *type);

>> +int aarch64_insn_decode_load_store_pair(u32 insn,

>> +					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);

>>   #endif /* __ASSEMBLY__ */

>>

>>   #endif	/* __ASM_INSN_H */

>> diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c

>> index c08b9ad..b56a66c 100644

>> --- a/arch/arm64/kernel/insn.c

>> +++ b/arch/arm64/kernel/insn.c

>> @@ -33,6 +33,7 @@

>>   #include <asm/insn.h>

>>

>>   #define AARCH64_INSN_SF_BIT	BIT(31)

>> +#define AARCH64_INSN_S_BIT	BIT(29)

>>   #define AARCH64_INSN_N_BIT	BIT(22)

>>

>>   static int aarch64_insn_encoding_class[] = {

>> @@ -1141,3 +1142,104 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn)

>>   {

>>   	return insn & CRM_MASK;

>>   }

>> +

>> +enum aarch64_insn_register aarch64_insn_extract_reg_num(u32 insn,

>> +				enum aarch64_insn_register_type type)

>

> Maybe call this aarch64_insn_decode_register? You're basically building

> decoders for some of the *_encode_* functions we already have.


OK, will fix it.
But please note that there are already some functions named
aarch32_insn_extract_reg_num() et al.

>> +{

>> +	int shift;

>> +

>> +	switch (type) {

>> +	case AARCH64_INSN_REGTYPE_RT:

>> +	case AARCH64_INSN_REGTYPE_RD:

>> +		shift = 0;

>> +		break;

>> +	case AARCH64_INSN_REGTYPE_RN:

>> +		shift = 5;

>> +		break;

>> +	case AARCH64_INSN_REGTYPE_RT2:

>> +	case AARCH64_INSN_REGTYPE_RA:

>> +		shift = 10;

>> +		break;

>> +	case AARCH64_INSN_REGTYPE_RM:

>> +		shift = 16;

>> +		break;

>> +	default:

>> +		pr_err("%s: unknown register type decoding %d\n", __func__,

>> +		       type);

>> +		return ~0L;

>> +	}

>

> This is largely a copy-paste from aarch64_insn_encode_register. Can you

> either work with what we have or factor out the common parts, please?


OK, will re-factor it.

>> +	return (insn & (GENMASK(4, 0) << shift)) >> shift;

>> +}

>> +

>> +int aarch64_insn_decode_add_sub_imm(u32 insn,

>

> This one could be aarch64_insn_extract_add_sub_imm, if you like.

> Then extract is the converse of gen and decode is the converse of encode.


I agree, will change the name as you suggest.
But please see the comment above about aarch32_insn_extract_*.

>> +				    enum aarch64_insn_register *dst,

>> +				    enum aarch64_insn_register *src,

>> +				    int *imm,

>> +				    enum aarch64_insn_variant *variant,

>> +				    enum aarch64_insn_adsb_type *type)

>> +{

>> +	if (aarch64_insn_is_add_imm(insn))

>> +		*type =	((insn) & AARCH64_INSN_S_BIT) ?

>> +				AARCH64_INSN_ADSB_ADD_SETFLAGS :

>> +				AARCH64_INSN_ADSB_ADD;

>> +	else if (aarch64_insn_is_sub_imm(insn))

>> +		*type =	((insn) & AARCH64_INSN_S_BIT) ?

>> +				AARCH64_INSN_ADSB_SUB_SETFLAGS :

>> +				AARCH64_INSN_ADSB_SUB;

>> +	else

>> +		return 0;

>

> Why do we need to return 0 on error? -EINVAL might make more sense.


I think that, in most failure cases of *_gen_*, 0 will be returned.
See aarch64_insn_encode_immediate() and aach64_insn_encode_register().
But returning -EINVAL is also understandable. Will change so.

>> +

>> +	*variant = (insn & AARCH64_INSN_SF_BIT) ? AARCH64_INSN_VARIANT_64BIT :

>> +					AARCH64_INSN_VARIANT_32BIT;

>> +

>> +	*dst = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RD);

>> +

>> +	*src = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RN);

>> +

>> +	/* TODO: ignore shilft field[23:22] */

>

> I don't understand the TODO.


Haha, we don't have to interpret 'shift' in a function prologue usage,
but yes, will fix it for general cases.

>> +	*imm = (int)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_12, insn);

>

> We use u64 for imm everywhere else.


Not true.
See aarch64_insn_gen_add_sub_imm(), _bitfield() and _movewide().

>> +	return 1;

>> +}


return 0;

> Similarly here, you're trying to implement the converse of

> aarch64_insn_gen_add_sub_imm


Will change the name.

>> +

>> +int aarch64_insn_decode_load_store_pair(u32 insn,

>> +					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)

>> +{

>> +	int imm;

>> +

>> +	if (aarch64_insn_is_stp(insn))

>> +		*type = AARCH64_INSN_LDST_STORE_PAIR;

>> +	else if (aarch64_insn_is_stp_post(insn))

>> +		*type = AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX;

>> +	else if (aarch64_insn_is_stp_pre(insn))

>> +		*type = AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX;

>> +	else if (aarch64_insn_is_ldp(insn))

>> +		*type = AARCH64_INSN_LDST_LOAD_PAIR;

>> +	else if (aarch64_insn_is_ldp_post(insn))

>> +		*type = AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX;

>> +	else if (aarch64_insn_is_ldp_pre(insn))

>> +		*type = AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX;

>> +	else

>> +		return 0;


return -EINVAL;

>> +	*variant = (insn & AARCH64_INSN_S_BIT) ? AARCH64_INSN_VARIANT_64BIT :

>> +				AARCH64_INSN_VARIANT_32BIT;

>> +

>> +	*reg1 = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RT);

>> +

>> +	*reg2 = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RT2);

>> +

>> +	*base = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RN);

>> +

>> +	imm = (int)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_7, insn);

>> +	asm("sbfm %0, %0, 0, 6" : "+r" (imm));

>

> What? Can't you write this in C?


OK, will use sign_extend64().

Thanks,
-Takahiro AKASHI

> Will

>


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
diff mbox

Patch

diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 30e50eb..8d5c538 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -165,6 +165,8 @@  enum aarch64_insn_ldst_type {
 	AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX,
 	AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX,
 	AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX,
+	AARCH64_INSN_LDST_LOAD_PAIR,
+	AARCH64_INSN_LDST_STORE_PAIR,
 };
 
 enum aarch64_insn_adsb_type {
@@ -225,6 +227,8 @@  static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \
 
 __AARCH64_INSN_FUNCS(str_reg,	0x3FE0EC00, 0x38206800)
 __AARCH64_INSN_FUNCS(ldr_reg,	0x3FE0EC00, 0x38606800)
+__AARCH64_INSN_FUNCS(stp,	0x7FC00000, 0x29000000)
+__AARCH64_INSN_FUNCS(ldp,	0x7FC00000, 0x29400000)
 __AARCH64_INSN_FUNCS(stp_post,	0x7FC00000, 0x28800000)
 __AARCH64_INSN_FUNCS(ldp_post,	0x7FC00000, 0x28C00000)
 __AARCH64_INSN_FUNCS(stp_pre,	0x7FC00000, 0x29800000)
@@ -277,6 +281,7 @@  __AARCH64_INSN_FUNCS(hint,	0xFFFFF01F, 0xD503201F)
 __AARCH64_INSN_FUNCS(br,	0xFFFFFC1F, 0xD61F0000)
 __AARCH64_INSN_FUNCS(blr,	0xFFFFFC1F, 0xD63F0000)
 __AARCH64_INSN_FUNCS(ret,	0xFFFFFC1F, 0xD65F0000)
+__AARCH64_INSN_FUNCS(eret,	0xFFFFFFFF, 0xD69F00E0)
 
 #undef	__AARCH64_INSN_FUNCS
 
@@ -370,6 +375,19 @@  bool aarch32_insn_is_wide(u32 insn);
 u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
 u32 aarch32_insn_mcr_extract_opc2(u32 insn);
 u32 aarch32_insn_mcr_extract_crm(u32 insn);
+int aarch64_insn_decode_add_sub_imm(u32 insn,
+				    enum aarch64_insn_register *dst,
+				    enum aarch64_insn_register *src,
+				    int *imm,
+				    enum aarch64_insn_variant *variant,
+				    enum aarch64_insn_adsb_type *type);
+int aarch64_insn_decode_load_store_pair(u32 insn,
+					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);
 #endif /* __ASSEMBLY__ */
 
 #endif	/* __ASM_INSN_H */
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index c08b9ad..b56a66c 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -33,6 +33,7 @@ 
 #include <asm/insn.h>
 
 #define AARCH64_INSN_SF_BIT	BIT(31)
+#define AARCH64_INSN_S_BIT	BIT(29)
 #define AARCH64_INSN_N_BIT	BIT(22)
 
 static int aarch64_insn_encoding_class[] = {
@@ -1141,3 +1142,104 @@  u32 aarch32_insn_mcr_extract_crm(u32 insn)
 {
 	return insn & CRM_MASK;
 }
+
+enum aarch64_insn_register aarch64_insn_extract_reg_num(u32 insn,
+				enum aarch64_insn_register_type type)
+{
+	int shift;
+
+	switch (type) {
+	case AARCH64_INSN_REGTYPE_RT:
+	case AARCH64_INSN_REGTYPE_RD:
+		shift = 0;
+		break;
+	case AARCH64_INSN_REGTYPE_RN:
+		shift = 5;
+		break;
+	case AARCH64_INSN_REGTYPE_RT2:
+	case AARCH64_INSN_REGTYPE_RA:
+		shift = 10;
+		break;
+	case AARCH64_INSN_REGTYPE_RM:
+		shift = 16;
+		break;
+	default:
+		pr_err("%s: unknown register type decoding %d\n", __func__,
+		       type);
+		return ~0L;
+	}
+
+	return (insn & (GENMASK(4, 0) << shift)) >> shift;
+}
+
+int aarch64_insn_decode_add_sub_imm(u32 insn,
+				    enum aarch64_insn_register *dst,
+				    enum aarch64_insn_register *src,
+				    int *imm,
+				    enum aarch64_insn_variant *variant,
+				    enum aarch64_insn_adsb_type *type)
+{
+	if (aarch64_insn_is_add_imm(insn))
+		*type =	((insn) & AARCH64_INSN_S_BIT) ?
+				AARCH64_INSN_ADSB_ADD_SETFLAGS :
+				AARCH64_INSN_ADSB_ADD;
+	else if (aarch64_insn_is_sub_imm(insn))
+		*type =	((insn) & AARCH64_INSN_S_BIT) ?
+				AARCH64_INSN_ADSB_SUB_SETFLAGS :
+				AARCH64_INSN_ADSB_SUB;
+	else
+		return 0;
+
+	*variant = (insn & AARCH64_INSN_SF_BIT) ? AARCH64_INSN_VARIANT_64BIT :
+					AARCH64_INSN_VARIANT_32BIT;
+
+	*dst = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RD);
+
+	*src = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RN);
+
+	/* TODO: ignore shilft field[23:22] */
+	*imm = (int)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_12, insn);
+
+	return 1;
+}
+
+int aarch64_insn_decode_load_store_pair(u32 insn,
+					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)
+{
+	int imm;
+
+	if (aarch64_insn_is_stp(insn))
+		*type = AARCH64_INSN_LDST_STORE_PAIR;
+	else if (aarch64_insn_is_stp_post(insn))
+		*type = AARCH64_INSN_LDST_STORE_PAIR_POST_INDEX;
+	else if (aarch64_insn_is_stp_pre(insn))
+		*type = AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX;
+	else if (aarch64_insn_is_ldp(insn))
+		*type = AARCH64_INSN_LDST_LOAD_PAIR;
+	else if (aarch64_insn_is_ldp_post(insn))
+		*type = AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX;
+	else if (aarch64_insn_is_ldp_pre(insn))
+		*type = AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX;
+	else
+		return 0;
+
+	*variant = (insn & AARCH64_INSN_S_BIT) ? AARCH64_INSN_VARIANT_64BIT :
+				AARCH64_INSN_VARIANT_32BIT;
+
+	*reg1 = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RT);
+
+	*reg2 = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RT2);
+
+	*base = aarch64_insn_extract_reg_num(insn, AARCH64_INSN_REGTYPE_RN);
+
+	imm = (int)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_7, insn);
+	asm("sbfm %0, %0, 0, 6" : "+r" (imm));
+	*offset = imm * 8;
+
+	return 1;
+}