arm64: Add LDR (literal) instruction
authorXu Kuohai <xukuohai@huawei.com>
Mon, 11 Jul 2022 15:08:21 +0000 (11:08 -0400)
committerDaniel Borkmann <daniel@iogearbox.net>
Mon, 11 Jul 2022 19:04:58 +0000 (21:04 +0200)
Add LDR (literal) instruction to load data from address relative to PC.
This instruction will be used to implement long jump from bpf prog to
bpf trampoline in the follow-up patch.

The instruction encoding:

    3       2   2     2                                     0        0
    0       7   6     4                                     5        0
+-----+-------+---+-----+-------------------------------------+--------+
| 0 x | 0 1 1 | 0 | 0 0 |                imm19                |   Rt   |
+-----+-------+---+-----+-------------------------------------+--------+

for 32-bit, variant x == 0; for 64-bit, x == 1.

branch_imm_common() is used to check the distance between pc and target
address, since it's reused by this patch and LDR (literal) is not a branch
instruction, rename it to label_imm_common().

Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
Acked-by: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/bpf/20220711150823.2128542-3-xukuohai@huawei.com
arch/arm64/include/asm/insn.h
arch/arm64/lib/insn.c

index 6aa2dc8..834bff7 100644 (file)
@@ -510,6 +510,9 @@ u32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,
                                    unsigned int imm,
                                    enum aarch64_insn_size_type size,
                                    enum aarch64_insn_ldst_type type);
+u32 aarch64_insn_gen_load_literal(unsigned long pc, unsigned long addr,
+                                 enum aarch64_insn_register reg,
+                                 bool is64bit);
 u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,
                                     enum aarch64_insn_register reg2,
                                     enum aarch64_insn_register base,
index 695d736..49e972b 100644 (file)
@@ -323,7 +323,7 @@ static u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type,
        return insn;
 }
 
-static inline long branch_imm_common(unsigned long pc, unsigned long addr,
+static inline long label_imm_common(unsigned long pc, unsigned long addr,
                                     long range)
 {
        long offset;
@@ -354,7 +354,7 @@ u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
         * ARM64 virtual address arrangement guarantees all kernel and module
         * texts are within +/-128M.
         */
-       offset = branch_imm_common(pc, addr, SZ_128M);
+       offset = label_imm_common(pc, addr, SZ_128M);
        if (offset >= SZ_128M)
                return AARCH64_BREAK_FAULT;
 
@@ -382,7 +382,7 @@ u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
        u32 insn;
        long offset;
 
-       offset = branch_imm_common(pc, addr, SZ_1M);
+       offset = label_imm_common(pc, addr, SZ_1M);
        if (offset >= SZ_1M)
                return AARCH64_BREAK_FAULT;
 
@@ -421,7 +421,7 @@ u32 aarch64_insn_gen_cond_branch_imm(unsigned long pc, unsigned long addr,
        u32 insn;
        long offset;
 
-       offset = branch_imm_common(pc, addr, SZ_1M);
+       offset = label_imm_common(pc, addr, SZ_1M);
 
        insn = aarch64_insn_get_bcond_value();
 
@@ -543,6 +543,28 @@ u32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,
        return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm);
 }
 
+u32 aarch64_insn_gen_load_literal(unsigned long pc, unsigned long addr,
+                                 enum aarch64_insn_register reg,
+                                 bool is64bit)
+{
+       u32 insn;
+       long offset;
+
+       offset = label_imm_common(pc, addr, SZ_1M);
+       if (offset >= SZ_1M)
+               return AARCH64_BREAK_FAULT;
+
+       insn = aarch64_insn_get_ldr_lit_value();
+
+       if (is64bit)
+               insn |= BIT(30);
+
+       insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
+
+       return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
+                                            offset >> 2);
+}
+
 u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,
                                     enum aarch64_insn_register reg2,
                                     enum aarch64_insn_register base,