arm64/insn: add support for emitting ADR/ADRP instructions
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Thu, 22 Nov 2018 08:46:45 +0000 (09:46 +0100)
committerWill Deacon <will.deacon@arm.com>
Tue, 27 Nov 2018 18:47:33 +0000 (18:47 +0000)
Add support for emitting ADR and ADRP instructions so we can switch
over our PLT generation code in a subsequent patch.

Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
arch/arm64/include/asm/insn.h
arch/arm64/kernel/insn.c

index c6802dea6cabd39cc271fee36eb75eccbdd0eaa9..9c01f04db64dbeef606488fc177c2594cd6ec1c2 100644 (file)
@@ -261,6 +261,11 @@ enum aarch64_insn_prfm_policy {
        AARCH64_INSN_PRFM_POLICY_STRM,
 };
 
+enum aarch64_insn_adr_type {
+       AARCH64_INSN_ADR_TYPE_ADRP,
+       AARCH64_INSN_ADR_TYPE_ADR,
+};
+
 #define        __AARCH64_INSN_FUNCS(abbr, mask, val)   \
 static __always_inline bool aarch64_insn_is_##abbr(u32 code) \
 { return (code & (mask)) == (val); } \
@@ -393,6 +398,9 @@ u32 aarch64_insn_gen_add_sub_imm(enum aarch64_insn_register dst,
                                 enum aarch64_insn_register src,
                                 int imm, enum aarch64_insn_variant variant,
                                 enum aarch64_insn_adsb_type type);
+u32 aarch64_insn_gen_adr(unsigned long pc, unsigned long addr,
+                        enum aarch64_insn_register reg,
+                        enum aarch64_insn_adr_type type);
 u32 aarch64_insn_gen_bitfield(enum aarch64_insn_register dst,
                              enum aarch64_insn_register src,
                              int immr, int imms,
index 2b3413549734522cae30cf1dcd36093245c9939b..7820a4a688faaa2edbf0771902eb14b5f66aec0a 100644 (file)
@@ -1239,6 +1239,35 @@ u32 aarch64_insn_gen_logical_shifted_reg(enum aarch64_insn_register dst,
        return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_6, insn, shift);
 }
 
+u32 aarch64_insn_gen_adr(unsigned long pc, unsigned long addr,
+                        enum aarch64_insn_register reg,
+                        enum aarch64_insn_adr_type type)
+{
+       u32 insn;
+       s32 offset;
+
+       switch (type) {
+       case AARCH64_INSN_ADR_TYPE_ADR:
+               insn = aarch64_insn_get_adr_value();
+               offset = addr - pc;
+               break;
+       case AARCH64_INSN_ADR_TYPE_ADRP:
+               insn = aarch64_insn_get_adrp_value();
+               offset = (addr - ALIGN_DOWN(pc, SZ_4K)) >> 12;
+               break;
+       default:
+               pr_err("%s: unknown adr encoding %d\n", __func__, type);
+               return AARCH64_BREAK_FAULT;
+       }
+
+       if (offset < -SZ_1M || offset >= SZ_1M)
+               return AARCH64_BREAK_FAULT;
+
+       insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RD, insn, reg);
+
+       return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_ADR, insn, offset);
+}
+
 /*
  * Decode the imm field of a branch, and return the byte offset as a
  * signed value (so it can be used when computing a new branch