2626#define TMP_REG_2 (MAX_BPF_JIT_REG + 1)
2727#define TCALL_CNT (MAX_BPF_JIT_REG + 2)
2828#define TMP_REG_3 (MAX_BPF_JIT_REG + 3)
29+ #define FP_BOTTOM (MAX_BPF_JIT_REG + 4)
2930
3031#define check_imm (bits , imm ) do { \
3132 if ((((imm) > 0) && ((imm) >> (bits))) || \
@@ -63,6 +64,7 @@ static const int bpf2a64[] = {
6364 [TCALL_CNT ] = A64_R (26 ),
6465 /* temporary register for blinding constants */
6566 [BPF_REG_AX ] = A64_R (9 ),
67+ [FP_BOTTOM ] = A64_R (27 ),
6668};
6769
6870struct jit_ctx {
@@ -73,6 +75,7 @@ struct jit_ctx {
7375 int exentry_idx ;
7476 __le32 * image ;
7577 u32 stack_size ;
78+ int fpb_offset ;
7679};
7780
7881static inline void emit (const u32 insn , struct jit_ctx * ctx )
@@ -218,7 +221,7 @@ static bool is_addsub_imm(u32 imm)
218221 *
219222 * offset = (u64)imm12 << scale
220223 */
221- static bool is_lsi_offset (s16 offset , int scale )
224+ static bool is_lsi_offset (int offset , int scale )
222225{
223226 if (offset < 0 )
224227 return false;
@@ -234,9 +237,9 @@ static bool is_lsi_offset(s16 offset, int scale)
234237
235238/* Tail call offset to jump into */
236239#if IS_ENABLED (CONFIG_ARM64_BTI_KERNEL )
237- #define PROLOGUE_OFFSET 8
240+ #define PROLOGUE_OFFSET 10
238241#else
239- #define PROLOGUE_OFFSET 7
242+ #define PROLOGUE_OFFSET 9
240243#endif
241244
242245static int build_prologue (struct jit_ctx * ctx , bool ebpf_from_cbpf )
@@ -248,6 +251,7 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
248251 const u8 r9 = bpf2a64 [BPF_REG_9 ];
249252 const u8 fp = bpf2a64 [BPF_REG_FP ];
250253 const u8 tcc = bpf2a64 [TCALL_CNT ];
254+ const u8 fpb = bpf2a64 [FP_BOTTOM ];
251255 const int idx0 = ctx -> idx ;
252256 int cur_offset ;
253257
@@ -286,9 +290,11 @@ static int build_prologue(struct jit_ctx *ctx, bool ebpf_from_cbpf)
286290 emit (A64_PUSH (r6 , r7 , A64_SP ), ctx );
287291 emit (A64_PUSH (r8 , r9 , A64_SP ), ctx );
288292 emit (A64_PUSH (fp , tcc , A64_SP ), ctx );
293+ emit (A64_PUSH (fpb , A64_R (28 ), A64_SP ), ctx );
289294
290295 /* Set up BPF prog stack base register */
291296 emit (A64_MOV (1 , fp , A64_SP ), ctx );
297+ emit (A64_SUB_I (1 , fpb , fp , ctx -> fpb_offset ), ctx );
292298
293299 if (!ebpf_from_cbpf ) {
294300 /* Initialize tail_call_cnt */
@@ -553,10 +559,13 @@ static void build_epilogue(struct jit_ctx *ctx)
553559 const u8 r8 = bpf2a64 [BPF_REG_8 ];
554560 const u8 r9 = bpf2a64 [BPF_REG_9 ];
555561 const u8 fp = bpf2a64 [BPF_REG_FP ];
562+ const u8 fpb = bpf2a64 [FP_BOTTOM ];
556563
557564 /* We're done with BPF stack */
558565 emit (A64_ADD_I (1 , A64_SP , A64_SP , ctx -> stack_size ), ctx );
559566
567+ /* Restore x27 and x28 */
568+ emit (A64_POP (fpb , A64_R (28 ), A64_SP ), ctx );
560569 /* Restore fs (x25) and x26 */
561570 emit (A64_POP (fp , A64_R (26 ), A64_SP ), ctx );
562571
@@ -650,6 +659,8 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
650659 const u8 src = bpf2a64 [insn -> src_reg ];
651660 const u8 tmp = bpf2a64 [TMP_REG_1 ];
652661 const u8 tmp2 = bpf2a64 [TMP_REG_2 ];
662+ const u8 fp = bpf2a64 [BPF_REG_FP ];
663+ const u8 fpb = bpf2a64 [FP_BOTTOM ];
653664 const s16 off = insn -> off ;
654665 const s32 imm = insn -> imm ;
655666 const int i = insn - ctx -> prog -> insnsi ;
@@ -658,6 +669,9 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
658669 u8 jmp_cond ;
659670 s32 jmp_offset ;
660671 u32 a64_insn ;
672+ u8 src_adj ;
673+ u8 dst_adj ;
674+ int off_adj ;
661675 int ret ;
662676
663677 switch (code ) {
@@ -1012,34 +1026,41 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
10121026 case BPF_LDX | BPF_PROBE_MEM | BPF_W :
10131027 case BPF_LDX | BPF_PROBE_MEM | BPF_H :
10141028 case BPF_LDX | BPF_PROBE_MEM | BPF_B :
1029+ if (ctx -> fpb_offset > 0 && src == fp ) {
1030+ src_adj = fpb ;
1031+ off_adj = off + ctx -> fpb_offset ;
1032+ } else {
1033+ src_adj = src ;
1034+ off_adj = off ;
1035+ }
10151036 switch (BPF_SIZE (code )) {
10161037 case BPF_W :
1017- if (is_lsi_offset (off , 2 )) {
1018- emit (A64_LDR32I (dst , src , off ), ctx );
1038+ if (is_lsi_offset (off_adj , 2 )) {
1039+ emit (A64_LDR32I (dst , src_adj , off_adj ), ctx );
10191040 } else {
10201041 emit_a64_mov_i (1 , tmp , off , ctx );
10211042 emit (A64_LDR32 (dst , src , tmp ), ctx );
10221043 }
10231044 break ;
10241045 case BPF_H :
1025- if (is_lsi_offset (off , 1 )) {
1026- emit (A64_LDRHI (dst , src , off ), ctx );
1046+ if (is_lsi_offset (off_adj , 1 )) {
1047+ emit (A64_LDRHI (dst , src_adj , off_adj ), ctx );
10271048 } else {
10281049 emit_a64_mov_i (1 , tmp , off , ctx );
10291050 emit (A64_LDRH (dst , src , tmp ), ctx );
10301051 }
10311052 break ;
10321053 case BPF_B :
1033- if (is_lsi_offset (off , 0 )) {
1034- emit (A64_LDRBI (dst , src , off ), ctx );
1054+ if (is_lsi_offset (off_adj , 0 )) {
1055+ emit (A64_LDRBI (dst , src_adj , off_adj ), ctx );
10351056 } else {
10361057 emit_a64_mov_i (1 , tmp , off , ctx );
10371058 emit (A64_LDRB (dst , src , tmp ), ctx );
10381059 }
10391060 break ;
10401061 case BPF_DW :
1041- if (is_lsi_offset (off , 3 )) {
1042- emit (A64_LDR64I (dst , src , off ), ctx );
1062+ if (is_lsi_offset (off_adj , 3 )) {
1063+ emit (A64_LDR64I (dst , src_adj , off_adj ), ctx );
10431064 } else {
10441065 emit_a64_mov_i (1 , tmp , off , ctx );
10451066 emit (A64_LDR64 (dst , src , tmp ), ctx );
@@ -1070,36 +1091,43 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
10701091 case BPF_ST | BPF_MEM | BPF_H :
10711092 case BPF_ST | BPF_MEM | BPF_B :
10721093 case BPF_ST | BPF_MEM | BPF_DW :
1094+ if (ctx -> fpb_offset > 0 && dst == fp ) {
1095+ dst_adj = fpb ;
1096+ off_adj = off + ctx -> fpb_offset ;
1097+ } else {
1098+ dst_adj = dst ;
1099+ off_adj = off ;
1100+ }
10731101 /* Load imm to a register then store it */
10741102 emit_a64_mov_i (1 , tmp , imm , ctx );
10751103 switch (BPF_SIZE (code )) {
10761104 case BPF_W :
1077- if (is_lsi_offset (off , 2 )) {
1078- emit (A64_STR32I (tmp , dst , off ), ctx );
1105+ if (is_lsi_offset (off_adj , 2 )) {
1106+ emit (A64_STR32I (tmp , dst_adj , off_adj ), ctx );
10791107 } else {
10801108 emit_a64_mov_i (1 , tmp2 , off , ctx );
10811109 emit (A64_STR32 (tmp , dst , tmp2 ), ctx );
10821110 }
10831111 break ;
10841112 case BPF_H :
1085- if (is_lsi_offset (off , 1 )) {
1086- emit (A64_STRHI (tmp , dst , off ), ctx );
1113+ if (is_lsi_offset (off_adj , 1 )) {
1114+ emit (A64_STRHI (tmp , dst_adj , off_adj ), ctx );
10871115 } else {
10881116 emit_a64_mov_i (1 , tmp2 , off , ctx );
10891117 emit (A64_STRH (tmp , dst , tmp2 ), ctx );
10901118 }
10911119 break ;
10921120 case BPF_B :
1093- if (is_lsi_offset (off , 0 )) {
1094- emit (A64_STRBI (tmp , dst , off ), ctx );
1121+ if (is_lsi_offset (off_adj , 0 )) {
1122+ emit (A64_STRBI (tmp , dst_adj , off_adj ), ctx );
10951123 } else {
10961124 emit_a64_mov_i (1 , tmp2 , off , ctx );
10971125 emit (A64_STRB (tmp , dst , tmp2 ), ctx );
10981126 }
10991127 break ;
11001128 case BPF_DW :
1101- if (is_lsi_offset (off , 3 )) {
1102- emit (A64_STR64I (tmp , dst , off ), ctx );
1129+ if (is_lsi_offset (off_adj , 3 )) {
1130+ emit (A64_STR64I (tmp , dst_adj , off_adj ), ctx );
11031131 } else {
11041132 emit_a64_mov_i (1 , tmp2 , off , ctx );
11051133 emit (A64_STR64 (tmp , dst , tmp2 ), ctx );
@@ -1113,34 +1141,41 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
11131141 case BPF_STX | BPF_MEM | BPF_H :
11141142 case BPF_STX | BPF_MEM | BPF_B :
11151143 case BPF_STX | BPF_MEM | BPF_DW :
1144+ if (ctx -> fpb_offset > 0 && dst == fp ) {
1145+ dst_adj = fpb ;
1146+ off_adj = off + ctx -> fpb_offset ;
1147+ } else {
1148+ dst_adj = dst ;
1149+ off_adj = off ;
1150+ }
11161151 switch (BPF_SIZE (code )) {
11171152 case BPF_W :
1118- if (is_lsi_offset (off , 2 )) {
1119- emit (A64_STR32I (src , dst , off ), ctx );
1153+ if (is_lsi_offset (off_adj , 2 )) {
1154+ emit (A64_STR32I (src , dst_adj , off_adj ), ctx );
11201155 } else {
11211156 emit_a64_mov_i (1 , tmp , off , ctx );
11221157 emit (A64_STR32 (src , dst , tmp ), ctx );
11231158 }
11241159 break ;
11251160 case BPF_H :
1126- if (is_lsi_offset (off , 1 )) {
1127- emit (A64_STRHI (src , dst , off ), ctx );
1161+ if (is_lsi_offset (off_adj , 1 )) {
1162+ emit (A64_STRHI (src , dst_adj , off_adj ), ctx );
11281163 } else {
11291164 emit_a64_mov_i (1 , tmp , off , ctx );
11301165 emit (A64_STRH (src , dst , tmp ), ctx );
11311166 }
11321167 break ;
11331168 case BPF_B :
1134- if (is_lsi_offset (off , 0 )) {
1135- emit (A64_STRBI (src , dst , off ), ctx );
1169+ if (is_lsi_offset (off_adj , 0 )) {
1170+ emit (A64_STRBI (src , dst_adj , off_adj ), ctx );
11361171 } else {
11371172 emit_a64_mov_i (1 , tmp , off , ctx );
11381173 emit (A64_STRB (src , dst , tmp ), ctx );
11391174 }
11401175 break ;
11411176 case BPF_DW :
1142- if (is_lsi_offset (off , 3 )) {
1143- emit (A64_STR64I (src , dst , off ), ctx );
1177+ if (is_lsi_offset (off_adj , 3 )) {
1178+ emit (A64_STR64I (src , dst_adj , off_adj ), ctx );
11441179 } else {
11451180 emit_a64_mov_i (1 , tmp , off , ctx );
11461181 emit (A64_STR64 (src , dst , tmp ), ctx );
@@ -1167,6 +1202,70 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
11671202 return 0 ;
11681203}
11691204
1205+ /*
1206+ * Return 0 if FP may change at runtime, otherwise find the minimum negative
1207+ * offset to FP and converts it to positive number.
1208+ */
1209+ static int find_fpb_offset (struct bpf_prog * prog )
1210+ {
1211+ int i ;
1212+ int offset = 0 ;
1213+
1214+ for (i = 0 ; i < prog -> len ; i ++ ) {
1215+ const struct bpf_insn * insn = & prog -> insnsi [i ];
1216+ const u8 class = BPF_CLASS (insn -> code );
1217+ const u8 mode = BPF_MODE (insn -> code );
1218+ const u8 src = insn -> src_reg ;
1219+ const u8 dst = insn -> dst_reg ;
1220+ const s32 imm = insn -> imm ;
1221+ const s16 off = insn -> off ;
1222+
1223+ switch (class ) {
1224+ case BPF_STX :
1225+ case BPF_ST :
1226+ /* fp holds atomic operation result */
1227+ if (class == BPF_STX && mode == BPF_ATOMIC &&
1228+ ((imm == BPF_XCHG ||
1229+ imm == (BPF_FETCH | BPF_ADD ) ||
1230+ imm == (BPF_FETCH | BPF_AND ) ||
1231+ imm == (BPF_FETCH | BPF_XOR ) ||
1232+ imm == (BPF_FETCH | BPF_OR )) &&
1233+ src == BPF_REG_FP ))
1234+ return 0 ;
1235+
1236+ if (mode == BPF_MEM && dst == BPF_REG_FP &&
1237+ off < offset )
1238+ offset = insn -> off ;
1239+ break ;
1240+
1241+ case BPF_JMP32 :
1242+ case BPF_JMP :
1243+ break ;
1244+
1245+ case BPF_LDX :
1246+ case BPF_LD :
1247+ /* fp holds load result */
1248+ if (dst == BPF_REG_FP )
1249+ return 0 ;
1250+
1251+ if (class == BPF_LDX && mode == BPF_MEM &&
1252+ src == BPF_REG_FP && off < offset )
1253+ offset = off ;
1254+ break ;
1255+
1256+ case BPF_ALU :
1257+ case BPF_ALU64 :
1258+ default :
1259+ /* fp holds ALU result */
1260+ if (dst == BPF_REG_FP )
1261+ return 0 ;
1262+ }
1263+ }
1264+
1265+ /* safely be converted to a positive 'int', since insn->off is 's16' */
1266+ return - offset ;
1267+ }
1268+
11701269static int build_body (struct jit_ctx * ctx , bool extra_pass )
11711270{
11721271 const struct bpf_prog * prog = ctx -> prog ;
@@ -1288,6 +1387,8 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
12881387 goto out_off ;
12891388 }
12901389
1390+ ctx .fpb_offset = find_fpb_offset (prog );
1391+
12911392 /*
12921393 * 1. Initial fake pass to compute ctx->idx and ctx->offset.
12931394 *
0 commit comments