[RFC bpf-next 2/2] bpf, mips: Add BPF_MEMSX support to the JITs
From: Nicholas Dudar
Date: Thu Aug 20 2026 - 22:51:52 EST
BPF_LDX with BPF_MEMSX loads a signed byte, half-word, or word and
sign-extends it through the full 64-bit destination. Both MIPS JITs
predate this mode and reject all three verifier-valid encodings when
JIT execution is required.
Teach the narrow-load helpers to select signed native loads. MIPS64
gets the full result from lb, lh, or lw. MIPS32 loads the low half,
observes the MIPS I load-delay rule, and propagates bit 31 into the
high half. Keep BPF_MEMSX with BPF_DW unsupported, and leave ordinary
loads on their existing unsigned helper path.
The existing BPF_LDX_MEMSX byte, half-word, and word test_bpf cases
cover the verifier-valid widths.
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Nicholas Dudar <main.kalliope@xxxxxxxxx>
---
arch/mips/net/bpf_jit_comp32.c | 31 +++++++++++++++++++++++++++----
arch/mips/net/bpf_jit_comp64.c | 33 ++++++++++++++++++++++++++++-----
2 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/arch/mips/net/bpf_jit_comp32.c b/arch/mips/net/bpf_jit_comp32.c
index 48a3c834453f2..3de7aac72fb83 100644
--- a/arch/mips/net/bpf_jit_comp32.c
+++ b/arch/mips/net/bpf_jit_comp32.c
@@ -709,16 +709,23 @@ static void emit_trunc_r64(struct jit_context *ctx, const u8 dst[], u32 width)
/* Narrow load operation: dst = *(size *)(src + off) */
static void emit_ldx_narrow(struct jit_context *ctx,
- u8 dst, u8 src, s16 off, u8 size)
+ u8 dst, u8 src, s16 off, u8 size,
+ bool sign_extend)
{
switch (size) {
/* Load a byte */
case BPF_B:
- emit(ctx, lbu, dst, off, src);
+ if (sign_extend)
+ emit(ctx, lb, dst, off, src);
+ else
+ emit(ctx, lbu, dst, off, src);
break;
/* Load a half word */
case BPF_H:
- emit(ctx, lhu, dst, off, src);
+ if (sign_extend)
+ emit(ctx, lh, dst, off, src);
+ else
+ emit(ctx, lhu, dst, off, src);
break;
/* Load a word */
case BPF_W:
@@ -736,7 +743,7 @@ static void emit_ldx(struct jit_context *ctx,
case BPF_B:
case BPF_H:
case BPF_W:
- emit_ldx_narrow(ctx, lo(dst), src, off, size);
+ emit_ldx_narrow(ctx, lo(dst), src, off, size, false);
emit(ctx, move, hi(dst), MIPS_R_ZERO);
break;
/* Load a double word */
@@ -754,6 +761,16 @@ static void emit_ldx(struct jit_context *ctx,
clobber_reg64(ctx, dst);
}
+/* Load operation with sign extension: dst = *(signed size *)(src + off) */
+static void emit_ldsx(struct jit_context *ctx,
+ const u8 dst[], u8 src, s16 off, u8 size)
+{
+ emit_ldx_narrow(ctx, lo(dst), src, off, size, true);
+ emit_load_delay(ctx);
+ emit(ctx, sra, hi(dst), lo(dst), 31);
+ clobber_reg64(ctx, dst);
+}
+
/* Store operation: *(size *)(dst + off) = src */
static void emit_stx(struct jit_context *ctx,
const u8 dst, const u8 src[], s16 off, u8 size)
@@ -1725,6 +1742,12 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_LDX | BPF_MEM | BPF_DW:
emit_ldx(ctx, dst, lo(src), off, BPF_SIZE(code));
break;
+ /* LDSX: dst = *(signed size *)(src + off) */
+ case BPF_LDX | BPF_MEMSX | BPF_W:
+ case BPF_LDX | BPF_MEMSX | BPF_H:
+ case BPF_LDX | BPF_MEMSX | BPF_B:
+ emit_ldsx(ctx, dst, lo(src), off, BPF_SIZE(code));
+ break;
/* ST: *(size *)(dst + off) = imm */
case BPF_ST | BPF_MEM | BPF_W:
case BPF_ST | BPF_MEM | BPF_H:
diff --git a/arch/mips/net/bpf_jit_comp64.c b/arch/mips/net/bpf_jit_comp64.c
index 22fb58f970223..14ef3f475c1c1 100644
--- a/arch/mips/net/bpf_jit_comp64.c
+++ b/arch/mips/net/bpf_jit_comp64.c
@@ -400,20 +400,30 @@ static void emit_trunc_r64(struct jit_context *ctx, u8 dst, u32 width)
/* Narrow load operation: dst = *(size *)(src + off) */
static void emit_ldx_narrow(struct jit_context *ctx,
- u8 dst, u8 src, s16 off, u8 size)
+ u8 dst, u8 src, s16 off, u8 size,
+ bool sign_extend)
{
switch (size) {
/* Load a byte */
case BPF_B:
- emit(ctx, lbu, dst, off, src);
+ if (sign_extend)
+ emit(ctx, lb, dst, off, src);
+ else
+ emit(ctx, lbu, dst, off, src);
break;
/* Load a half word */
case BPF_H:
- emit(ctx, lhu, dst, off, src);
+ if (sign_extend)
+ emit(ctx, lh, dst, off, src);
+ else
+ emit(ctx, lhu, dst, off, src);
break;
/* Load a word */
case BPF_W:
- emit(ctx, lwu, dst, off, src);
+ if (sign_extend)
+ emit(ctx, lw, dst, off, src);
+ else
+ emit(ctx, lwu, dst, off, src);
break;
}
}
@@ -426,7 +436,7 @@ static void emit_ldx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
case BPF_B:
case BPF_H:
case BPF_W:
- emit_ldx_narrow(ctx, dst, src, off, size);
+ emit_ldx_narrow(ctx, dst, src, off, size, false);
break;
/* Load a double word */
case BPF_DW:
@@ -436,6 +446,13 @@ static void emit_ldx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
clobber_reg(ctx, dst);
}
+/* Load operation with sign extension: dst = *(signed size *)(src + off) */
+static void emit_ldsx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
+{
+ emit_ldx_narrow(ctx, dst, src, off, size, true);
+ clobber_reg(ctx, dst);
+}
+
/* Store operation: *(size *)(dst + off) = src */
static void emit_stx(struct jit_context *ctx, u8 dst, u8 src, s16 off, u8 size)
{
@@ -908,6 +925,12 @@ int build_insn(const struct bpf_insn *insn, struct jit_context *ctx)
case BPF_LDX | BPF_MEM | BPF_DW:
emit_ldx(ctx, dst, src, off, BPF_SIZE(code));
break;
+ /* LDSX: dst = *(signed size *)(src + off) */
+ case BPF_LDX | BPF_MEMSX | BPF_W:
+ case BPF_LDX | BPF_MEMSX | BPF_H:
+ case BPF_LDX | BPF_MEMSX | BPF_B:
+ emit_ldsx(ctx, dst, src, off, BPF_SIZE(code));
+ break;
/* ST: *(size *)(dst + off) = imm */
case BPF_ST | BPF_MEM | BPF_W:
case BPF_ST | BPF_MEM | BPF_H: