]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
arm32, bpf: add support for sign-extension load instruction
authorPuranjay Mohan <puranjay12@gmail.com>
Thu, 7 Sep 2023 23:05:43 +0000 (23:05 +0000)
committerAlexei Starovoitov <ast@kernel.org>
Sat, 16 Sep 2023 00:16:56 +0000 (17:16 -0700)
The cpuv4 added the support of an instruction that is similar to load
but also sign-extends the result after the load.

BPF_MEMSX | <size> | BPF_LDX means dst = *(signed size *) (src + offset)
here <size> can be one of BPF_B, BPF_H, BPF_W.

ARM32 has instructions to load a byte or a half word with sign
extension into a 32bit register. As the JIT uses two 32 bit registers
to simulate a 64-bit BPF register, an extra instruction is emitted to
sign-extent the result up to the second register.

Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Link: https://lore.kernel.org/r/20230907230550.1417590-3-puranjay12@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
arch/arm/net/bpf_jit_32.c
arch/arm/net/bpf_jit_32.h

index c03600fe86f675f9da217fc6e20cd691eae5af7a..1b3bd05878ab98112933cc86994daebaa72aeacf 100644 (file)
@@ -333,6 +333,9 @@ static u32 arm_bpf_ldst_imm8(u32 op, u8 rt, u8 rn, s16 imm8)
 #define ARM_LDRD_I(rt, rn, off)        arm_bpf_ldst_imm8(ARM_INST_LDRD_I, rt, rn, off)
 #define ARM_LDRH_I(rt, rn, off)        arm_bpf_ldst_imm8(ARM_INST_LDRH_I, rt, rn, off)
 
+#define ARM_LDRSH_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_LDRSH_I, rt, rn, off)
+#define ARM_LDRSB_I(rt, rn, off) arm_bpf_ldst_imm8(ARM_INST_LDRSB_I, rt, rn, off)
+
 #define ARM_STR_I(rt, rn, off) arm_bpf_ldst_imm12(ARM_INST_STR_I, rt, rn, off)
 #define ARM_STRB_I(rt, rn, off)        arm_bpf_ldst_imm12(ARM_INST_STRB_I, rt, rn, off)
 #define ARM_STRD_I(rt, rn, off)        arm_bpf_ldst_imm8(ARM_INST_STRD_I, rt, rn, off)
@@ -1026,6 +1029,24 @@ static bool is_ldst_imm(s16 off, const u8 size)
        return -off_max <= off && off <= off_max;
 }
 
+static bool is_ldst_imm8(s16 off, const u8 size)
+{
+       s16 off_max = 0;
+
+       switch (size) {
+       case BPF_B:
+               off_max = 0xff;
+               break;
+       case BPF_W:
+               off_max = 0xfff;
+               break;
+       case BPF_H:
+               off_max = 0xff;
+               break;
+       }
+       return -off_max <= off && off <= off_max;
+}
+
 /* *(size *)(dst + off) = src */
 static inline void emit_str_r(const s8 dst, const s8 src[],
                              s16 off, struct jit_ctx *ctx, const u8 sz){
@@ -1105,6 +1126,50 @@ static inline void emit_ldx_r(const s8 dst[], const s8 src,
        arm_bpf_put_reg64(dst, rd, ctx);
 }
 
+/* dst = *(signed size*)(src + off) */
+static inline void emit_ldsx_r(const s8 dst[], const s8 src,
+                              s16 off, struct jit_ctx *ctx, const u8 sz){
+       const s8 *tmp = bpf2a32[TMP_REG_1];
+       const s8 *rd = is_stacked(dst_lo) ? tmp : dst;
+       s8 rm = src;
+       int add_off;
+
+       if (!is_ldst_imm8(off, sz)) {
+               /*
+                * offset does not fit in the load/store immediate,
+                * construct an ADD instruction to apply the offset.
+                */
+               add_off = imm8m(off);
+               if (add_off > 0) {
+                       emit(ARM_ADD_I(tmp[0], src, add_off), ctx);
+                       rm = tmp[0];
+               } else {
+                       emit_a32_mov_i(tmp[0], off, ctx);
+                       emit(ARM_ADD_R(tmp[0], tmp[0], src), ctx);
+                       rm = tmp[0];
+               }
+               off = 0;
+       }
+
+       switch (sz) {
+       case BPF_B:
+               /* Load a Byte with sign extension*/
+               emit(ARM_LDRSB_I(rd[1], rm, off), ctx);
+               break;
+       case BPF_H:
+               /* Load a HalfWord with sign extension*/
+               emit(ARM_LDRSH_I(rd[1], rm, off), ctx);
+               break;
+       case BPF_W:
+               /* Load a Word*/
+               emit(ARM_LDR_I(rd[1], rm, off), ctx);
+               break;
+       }
+       /* Carry the sign extension to upper 32 bits */
+       emit(ARM_ASR_I(rd[0], rd[1], 31), ctx);
+       arm_bpf_put_reg64(dst, rd, ctx);
+}
+
 /* Arithmatic Operation */
 static inline void emit_ar_r(const u8 rd, const u8 rt, const u8 rm,
                             const u8 rn, struct jit_ctx *ctx, u8 op,
@@ -1603,8 +1668,15 @@ exit:
        case BPF_LDX | BPF_MEM | BPF_H:
        case BPF_LDX | BPF_MEM | BPF_B:
        case BPF_LDX | BPF_MEM | BPF_DW:
+       /* LDSX: dst = *(signed size *)(src + off) */
+       case BPF_LDX | BPF_MEMSX | BPF_B:
+       case BPF_LDX | BPF_MEMSX | BPF_H:
+       case BPF_LDX | BPF_MEMSX | BPF_W:
                rn = arm_bpf_get_reg32(src_lo, tmp2[1], ctx);
-               emit_ldx_r(dst, rn, off, ctx, BPF_SIZE(code));
+               if (BPF_MODE(insn->code) == BPF_MEMSX)
+                       emit_ldsx_r(dst, rn, off, ctx, BPF_SIZE(code));
+               else
+                       emit_ldx_r(dst, rn, off, ctx, BPF_SIZE(code));
                break;
        /* speculation barrier */
        case BPF_ST | BPF_NOSPEC:
index e0b593a1498d11aa46e077dfab85caeb7d957e82..79c7373fadce2db1a3be185bea593f4bd756bd39 100644 (file)
 #define ARM_INST_LDST__IMM12   0x00000fff
 #define ARM_INST_LDRB_I                0x05500000
 #define ARM_INST_LDRB_R                0x07d00000
+#define ARM_INST_LDRSB_I       0x015000d0
 #define ARM_INST_LDRD_I                0x014000d0
 #define ARM_INST_LDRH_I                0x015000b0
 #define ARM_INST_LDRH_R                0x019000b0
+#define ARM_INST_LDRSH_I       0x015000f0
 #define ARM_INST_LDR_I         0x05100000
 #define ARM_INST_LDR_R         0x07900000