arm64, insn: Add ldr/str with immediate offset
authorXu Kuohai <xukuohai@huawei.com>
Mon, 21 Mar 2022 15:28:48 +0000 (11:28 -0400)
committerDaniel Borkmann <daniel@iogearbox.net>
Thu, 31 Mar 2022 22:27:29 +0000 (00:27 +0200)
This patch introduces ldr/str with immediate offset support to simplify
the JIT implementation of BPF LDX/STX instructions on arm64. Although
arm64 ldr/str immediate is available in pre-index, post-index and
unsigned offset forms, the unsigned offset form is sufficient for BPF,
so this patch only adds this type.

Signed-off-by: Xu Kuohai <xukuohai@huawei.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20220321152852.2334294-2-xukuohai@huawei.com
arch/arm64/include/asm/insn.h
arch/arm64/lib/insn.c

index 1e5760d..6aa2dc8 100644 (file)
@@ -201,6 +201,8 @@ enum aarch64_insn_size_type {
 enum aarch64_insn_ldst_type {
        AARCH64_INSN_LDST_LOAD_REG_OFFSET,
        AARCH64_INSN_LDST_STORE_REG_OFFSET,
+       AARCH64_INSN_LDST_LOAD_IMM_OFFSET,
+       AARCH64_INSN_LDST_STORE_IMM_OFFSET,
        AARCH64_INSN_LDST_LOAD_PAIR_PRE_INDEX,
        AARCH64_INSN_LDST_STORE_PAIR_PRE_INDEX,
        AARCH64_INSN_LDST_LOAD_PAIR_POST_INDEX,
@@ -335,6 +337,7 @@ __AARCH64_INSN_FUNCS(load_pre,      0x3FE00C00, 0x38400C00)
 __AARCH64_INSN_FUNCS(store_post,       0x3FE00C00, 0x38000400)
 __AARCH64_INSN_FUNCS(load_post,        0x3FE00C00, 0x38400400)
 __AARCH64_INSN_FUNCS(str_reg,  0x3FE0EC00, 0x38206800)
+__AARCH64_INSN_FUNCS(str_imm,  0x3FC00000, 0x39000000)
 __AARCH64_INSN_FUNCS(ldadd,    0x3F20FC00, 0x38200000)
 __AARCH64_INSN_FUNCS(ldclr,    0x3F20FC00, 0x38201000)
 __AARCH64_INSN_FUNCS(ldeor,    0x3F20FC00, 0x38202000)
@@ -342,6 +345,7 @@ __AARCH64_INSN_FUNCS(ldset, 0x3F20FC00, 0x38203000)
 __AARCH64_INSN_FUNCS(swp,      0x3F20FC00, 0x38208000)
 __AARCH64_INSN_FUNCS(cas,      0x3FA07C00, 0x08A07C00)
 __AARCH64_INSN_FUNCS(ldr_reg,  0x3FE0EC00, 0x38606800)
+__AARCH64_INSN_FUNCS(ldr_imm,  0x3FC00000, 0x39400000)
 __AARCH64_INSN_FUNCS(ldr_lit,  0xBF000000, 0x18000000)
 __AARCH64_INSN_FUNCS(ldrsw_lit,        0xFF000000, 0x98000000)
 __AARCH64_INSN_FUNCS(exclusive,        0x3F800000, 0x08000000)
@@ -501,6 +505,11 @@ u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg,
                                    enum aarch64_insn_register offset,
                                    enum aarch64_insn_size_type size,
                                    enum aarch64_insn_ldst_type type);
+u32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,
+                                   enum aarch64_insn_register base,
+                                   unsigned int imm,
+                                   enum aarch64_insn_size_type size,
+                                   enum aarch64_insn_ldst_type type);
 u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,
                                     enum aarch64_insn_register reg2,
                                     enum aarch64_insn_register base,
index 5e90887..695d736 100644 (file)
@@ -299,29 +299,24 @@ static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type,
        return insn;
 }
 
+static const u32 aarch64_insn_ldst_size[] = {
+       [AARCH64_INSN_SIZE_8] = 0,
+       [AARCH64_INSN_SIZE_16] = 1,
+       [AARCH64_INSN_SIZE_32] = 2,
+       [AARCH64_INSN_SIZE_64] = 3,
+};
+
 static u32 aarch64_insn_encode_ldst_size(enum aarch64_insn_size_type type,
                                         u32 insn)
 {
        u32 size;
 
-       switch (type) {
-       case AARCH64_INSN_SIZE_8:
-               size = 0;
-               break;
-       case AARCH64_INSN_SIZE_16:
-               size = 1;
-               break;
-       case AARCH64_INSN_SIZE_32:
-               size = 2;
-               break;
-       case AARCH64_INSN_SIZE_64:
-               size = 3;
-               break;
-       default:
+       if (type < AARCH64_INSN_SIZE_8 || type > AARCH64_INSN_SIZE_64) {
                pr_err("%s: unknown size encoding %d\n", __func__, type);
                return AARCH64_BREAK_FAULT;
        }
 
+       size = aarch64_insn_ldst_size[type];
        insn &= ~GENMASK(31, 30);
        insn |= size << 30;
 
@@ -504,6 +499,50 @@ u32 aarch64_insn_gen_load_store_reg(enum aarch64_insn_register reg,
                                            offset);
 }
 
+u32 aarch64_insn_gen_load_store_imm(enum aarch64_insn_register reg,
+                                   enum aarch64_insn_register base,
+                                   unsigned int imm,
+                                   enum aarch64_insn_size_type size,
+                                   enum aarch64_insn_ldst_type type)
+{
+       u32 insn;
+       u32 shift;
+
+       if (size < AARCH64_INSN_SIZE_8 || size > AARCH64_INSN_SIZE_64) {
+               pr_err("%s: unknown size encoding %d\n", __func__, type);
+               return AARCH64_BREAK_FAULT;
+       }
+
+       shift = aarch64_insn_ldst_size[size];
+       if (imm & ~(BIT(12 + shift) - BIT(shift))) {
+               pr_err("%s: invalid imm: %d\n", __func__, imm);
+               return AARCH64_BREAK_FAULT;
+       }
+
+       imm >>= shift;
+
+       switch (type) {
+       case AARCH64_INSN_LDST_LOAD_IMM_OFFSET:
+               insn = aarch64_insn_get_ldr_imm_value();
+               break;
+       case AARCH64_INSN_LDST_STORE_IMM_OFFSET:
+               insn = aarch64_insn_get_str_imm_value();
+               break;
+       default:
+               pr_err("%s: unknown load/store encoding %d\n", __func__, type);
+               return AARCH64_BREAK_FAULT;
+       }
+
+       insn = aarch64_insn_encode_ldst_size(size, insn);
+
+       insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RT, insn, reg);
+
+       insn = aarch64_insn_encode_register(AARCH64_INSN_REGTYPE_RN, insn,
+                                           base);
+
+       return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_12, insn, imm);
+}
+
 u32 aarch64_insn_gen_load_store_pair(enum aarch64_insn_register reg1,
                                     enum aarch64_insn_register reg2,
                                     enum aarch64_insn_register base,