|
27 | 27 | #define TCALL_CNT (MAX_BPF_JIT_REG + 2)
|
28 | 28 | #define TMP_REG_3 (MAX_BPF_JIT_REG + 3)
|
29 | 29 |
|
| 30 | +#define check_imm(bits, imm) do { \ |
| 31 | + if ((((imm) > 0) && ((imm) >> (bits))) || \ |
| 32 | + (((imm) < 0) && (~(imm) >> (bits)))) { \ |
| 33 | + pr_info("[%2d] imm=%d(0x%x) out of range\n", \ |
| 34 | + i, imm, imm); \ |
| 35 | + return -EINVAL; \ |
| 36 | + } \ |
| 37 | +} while (0) |
| 38 | +#define check_imm19(imm) check_imm(19, imm) |
| 39 | +#define check_imm26(imm) check_imm(26, imm) |
| 40 | + |
30 | 41 | /* Map BPF registers to A64 registers */
|
31 | 42 | static const int bpf2a64[] = {
|
32 | 43 | /* return value from in-kernel function, and exit value from eBPF */
|
@@ -329,6 +340,170 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
|
329 | 340 | #undef jmp_offset
|
330 | 341 | }
|
331 | 342 |
|
| 343 | +#ifdef CONFIG_ARM64_LSE_ATOMICS |
| 344 | +static int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) |
| 345 | +{ |
| 346 | + const u8 code = insn->code; |
| 347 | + const u8 dst = bpf2a64[insn->dst_reg]; |
| 348 | + const u8 src = bpf2a64[insn->src_reg]; |
| 349 | + const u8 tmp = bpf2a64[TMP_REG_1]; |
| 350 | + const u8 tmp2 = bpf2a64[TMP_REG_2]; |
| 351 | + const bool isdw = BPF_SIZE(code) == BPF_DW; |
| 352 | + const s16 off = insn->off; |
| 353 | + u8 reg; |
| 354 | + |
| 355 | + if (!off) { |
| 356 | + reg = dst; |
| 357 | + } else { |
| 358 | + emit_a64_mov_i(1, tmp, off, ctx); |
| 359 | + emit(A64_ADD(1, tmp, tmp, dst), ctx); |
| 360 | + reg = tmp; |
| 361 | + } |
| 362 | + |
| 363 | + switch (insn->imm) { |
| 364 | + /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ |
| 365 | + case BPF_ADD: |
| 366 | + emit(A64_STADD(isdw, reg, src), ctx); |
| 367 | + break; |
| 368 | + case BPF_AND: |
| 369 | + emit(A64_MVN(isdw, tmp2, src), ctx); |
| 370 | + emit(A64_STCLR(isdw, reg, tmp2), ctx); |
| 371 | + break; |
| 372 | + case BPF_OR: |
| 373 | + emit(A64_STSET(isdw, reg, src), ctx); |
| 374 | + break; |
| 375 | + case BPF_XOR: |
| 376 | + emit(A64_STEOR(isdw, reg, src), ctx); |
| 377 | + break; |
| 378 | + /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ |
| 379 | + case BPF_ADD | BPF_FETCH: |
| 380 | + emit(A64_LDADDAL(isdw, src, reg, src), ctx); |
| 381 | + break; |
| 382 | + case BPF_AND | BPF_FETCH: |
| 383 | + emit(A64_MVN(isdw, tmp2, src), ctx); |
| 384 | + emit(A64_LDCLRAL(isdw, src, reg, tmp2), ctx); |
| 385 | + break; |
| 386 | + case BPF_OR | BPF_FETCH: |
| 387 | + emit(A64_LDSETAL(isdw, src, reg, src), ctx); |
| 388 | + break; |
| 389 | + case BPF_XOR | BPF_FETCH: |
| 390 | + emit(A64_LDEORAL(isdw, src, reg, src), ctx); |
| 391 | + break; |
| 392 | + /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ |
| 393 | + case BPF_XCHG: |
| 394 | + emit(A64_SWPAL(isdw, src, reg, src), ctx); |
| 395 | + break; |
| 396 | + /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ |
| 397 | + case BPF_CMPXCHG: |
| 398 | + emit(A64_CASAL(isdw, src, reg, bpf2a64[BPF_REG_0]), ctx); |
| 399 | + break; |
| 400 | + default: |
| 401 | + pr_err_once("unknown atomic op code %02x\n", insn->imm); |
| 402 | + return -EINVAL; |
| 403 | + } |
| 404 | + |
| 405 | + return 0; |
| 406 | +} |
| 407 | +#else |
| 408 | +static inline int emit_lse_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) |
| 409 | +{ |
| 410 | + return -EINVAL; |
| 411 | +} |
| 412 | +#endif |
| 413 | + |
| 414 | +static int emit_ll_sc_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) |
| 415 | +{ |
| 416 | + const u8 code = insn->code; |
| 417 | + const u8 dst = bpf2a64[insn->dst_reg]; |
| 418 | + const u8 src = bpf2a64[insn->src_reg]; |
| 419 | + const u8 tmp = bpf2a64[TMP_REG_1]; |
| 420 | + const u8 tmp2 = bpf2a64[TMP_REG_2]; |
| 421 | + const u8 tmp3 = bpf2a64[TMP_REG_3]; |
| 422 | + const int i = insn - ctx->prog->insnsi; |
| 423 | + const s32 imm = insn->imm; |
| 424 | + const s16 off = insn->off; |
| 425 | + const bool isdw = BPF_SIZE(code) == BPF_DW; |
| 426 | + u8 reg; |
| 427 | + s32 jmp_offset; |
| 428 | + |
| 429 | + if (!off) { |
| 430 | + reg = dst; |
| 431 | + } else { |
| 432 | + emit_a64_mov_i(1, tmp, off, ctx); |
| 433 | + emit(A64_ADD(1, tmp, tmp, dst), ctx); |
| 434 | + reg = tmp; |
| 435 | + } |
| 436 | + |
| 437 | + if (imm == BPF_ADD || imm == BPF_AND || |
| 438 | + imm == BPF_OR || imm == BPF_XOR) { |
| 439 | + /* lock *(u32/u64 *)(dst_reg + off) <op>= src_reg */ |
| 440 | + emit(A64_LDXR(isdw, tmp2, reg), ctx); |
| 441 | + if (imm == BPF_ADD) |
| 442 | + emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); |
| 443 | + else if (imm == BPF_AND) |
| 444 | + emit(A64_AND(isdw, tmp2, tmp2, src), ctx); |
| 445 | + else if (imm == BPF_OR) |
| 446 | + emit(A64_ORR(isdw, tmp2, tmp2, src), ctx); |
| 447 | + else |
| 448 | + emit(A64_EOR(isdw, tmp2, tmp2, src), ctx); |
| 449 | + emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); |
| 450 | + jmp_offset = -3; |
| 451 | + check_imm19(jmp_offset); |
| 452 | + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); |
| 453 | + } else if (imm == (BPF_ADD | BPF_FETCH) || |
| 454 | + imm == (BPF_AND | BPF_FETCH) || |
| 455 | + imm == (BPF_OR | BPF_FETCH) || |
| 456 | + imm == (BPF_XOR | BPF_FETCH)) { |
| 457 | + /* src_reg = atomic_fetch_<op>(dst_reg + off, src_reg) */ |
| 458 | + const u8 ax = bpf2a64[BPF_REG_AX]; |
| 459 | + |
| 460 | + emit(A64_MOV(isdw, ax, src), ctx); |
| 461 | + emit(A64_LDXR(isdw, src, reg), ctx); |
| 462 | + if (imm == (BPF_ADD | BPF_FETCH)) |
| 463 | + emit(A64_ADD(isdw, tmp2, src, ax), ctx); |
| 464 | + else if (imm == (BPF_AND | BPF_FETCH)) |
| 465 | + emit(A64_AND(isdw, tmp2, src, ax), ctx); |
| 466 | + else if (imm == (BPF_OR | BPF_FETCH)) |
| 467 | + emit(A64_ORR(isdw, tmp2, src, ax), ctx); |
| 468 | + else |
| 469 | + emit(A64_EOR(isdw, tmp2, src, ax), ctx); |
| 470 | + emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); |
| 471 | + jmp_offset = -3; |
| 472 | + check_imm19(jmp_offset); |
| 473 | + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); |
| 474 | + emit(A64_DMB_ISH, ctx); |
| 475 | + } else if (imm == BPF_XCHG) { |
| 476 | + /* src_reg = atomic_xchg(dst_reg + off, src_reg); */ |
| 477 | + emit(A64_MOV(isdw, tmp2, src), ctx); |
| 478 | + emit(A64_LDXR(isdw, src, reg), ctx); |
| 479 | + emit(A64_STLXR(isdw, tmp2, reg, tmp3), ctx); |
| 480 | + jmp_offset = -2; |
| 481 | + check_imm19(jmp_offset); |
| 482 | + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); |
| 483 | + emit(A64_DMB_ISH, ctx); |
| 484 | + } else if (imm == BPF_CMPXCHG) { |
| 485 | + /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */ |
| 486 | + const u8 r0 = bpf2a64[BPF_REG_0]; |
| 487 | + |
| 488 | + emit(A64_MOV(isdw, tmp2, r0), ctx); |
| 489 | + emit(A64_LDXR(isdw, r0, reg), ctx); |
| 490 | + emit(A64_EOR(isdw, tmp3, r0, tmp2), ctx); |
| 491 | + jmp_offset = 4; |
| 492 | + check_imm19(jmp_offset); |
| 493 | + emit(A64_CBNZ(isdw, tmp3, jmp_offset), ctx); |
| 494 | + emit(A64_STLXR(isdw, src, reg, tmp3), ctx); |
| 495 | + jmp_offset = -4; |
| 496 | + check_imm19(jmp_offset); |
| 497 | + emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); |
| 498 | + emit(A64_DMB_ISH, ctx); |
| 499 | + } else { |
| 500 | + pr_err_once("unknown atomic op code %02x\n", imm); |
| 501 | + return -EINVAL; |
| 502 | + } |
| 503 | + |
| 504 | + return 0; |
| 505 | +} |
| 506 | + |
332 | 507 | static void build_epilogue(struct jit_ctx *ctx)
|
333 | 508 | {
|
334 | 509 | const u8 r0 = bpf2a64[BPF_REG_0];
|
@@ -434,29 +609,16 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
|
434 | 609 | const u8 src = bpf2a64[insn->src_reg];
|
435 | 610 | const u8 tmp = bpf2a64[TMP_REG_1];
|
436 | 611 | const u8 tmp2 = bpf2a64[TMP_REG_2];
|
437 |
| - const u8 tmp3 = bpf2a64[TMP_REG_3]; |
438 | 612 | const s16 off = insn->off;
|
439 | 613 | const s32 imm = insn->imm;
|
440 | 614 | const int i = insn - ctx->prog->insnsi;
|
441 | 615 | const bool is64 = BPF_CLASS(code) == BPF_ALU64 ||
|
442 | 616 | BPF_CLASS(code) == BPF_JMP;
|
443 |
| - const bool isdw = BPF_SIZE(code) == BPF_DW; |
444 |
| - u8 jmp_cond, reg; |
| 617 | + u8 jmp_cond; |
445 | 618 | s32 jmp_offset;
|
446 | 619 | u32 a64_insn;
|
447 | 620 | int ret;
|
448 | 621 |
|
449 |
| -#define check_imm(bits, imm) do { \ |
450 |
| - if ((((imm) > 0) && ((imm) >> (bits))) || \ |
451 |
| - (((imm) < 0) && (~(imm) >> (bits)))) { \ |
452 |
| - pr_info("[%2d] imm=%d(0x%x) out of range\n", \ |
453 |
| - i, imm, imm); \ |
454 |
| - return -EINVAL; \ |
455 |
| - } \ |
456 |
| -} while (0) |
457 |
| -#define check_imm19(imm) check_imm(19, imm) |
458 |
| -#define check_imm26(imm) check_imm(26, imm) |
459 |
| - |
460 | 622 | switch (code) {
|
461 | 623 | /* dst = src */
|
462 | 624 | case BPF_ALU | BPF_MOV | BPF_X:
|
@@ -891,33 +1053,12 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
|
891 | 1053 |
|
892 | 1054 | case BPF_STX | BPF_ATOMIC | BPF_W:
|
893 | 1055 | case BPF_STX | BPF_ATOMIC | BPF_DW:
|
894 |
| - if (insn->imm != BPF_ADD) { |
895 |
| - pr_err_once("unknown atomic op code %02x\n", insn->imm); |
896 |
| - return -EINVAL; |
897 |
| - } |
898 |
| - |
899 |
| - /* STX XADD: lock *(u32 *)(dst + off) += src |
900 |
| - * and |
901 |
| - * STX XADD: lock *(u64 *)(dst + off) += src |
902 |
| - */ |
903 |
| - |
904 |
| - if (!off) { |
905 |
| - reg = dst; |
906 |
| - } else { |
907 |
| - emit_a64_mov_i(1, tmp, off, ctx); |
908 |
| - emit(A64_ADD(1, tmp, tmp, dst), ctx); |
909 |
| - reg = tmp; |
910 |
| - } |
911 |
| - if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) { |
912 |
| - emit(A64_STADD(isdw, reg, src), ctx); |
913 |
| - } else { |
914 |
| - emit(A64_LDXR(isdw, tmp2, reg), ctx); |
915 |
| - emit(A64_ADD(isdw, tmp2, tmp2, src), ctx); |
916 |
| - emit(A64_STXR(isdw, tmp2, reg, tmp3), ctx); |
917 |
| - jmp_offset = -3; |
918 |
| - check_imm19(jmp_offset); |
919 |
| - emit(A64_CBNZ(0, tmp3, jmp_offset), ctx); |
920 |
| - } |
| 1056 | + if (cpus_have_cap(ARM64_HAS_LSE_ATOMICS)) |
| 1057 | + ret = emit_lse_atomic(insn, ctx); |
| 1058 | + else |
| 1059 | + ret = emit_ll_sc_atomic(insn, ctx); |
| 1060 | + if (ret) |
| 1061 | + return ret; |
921 | 1062 | break;
|
922 | 1063 |
|
923 | 1064 | default:
|
|
0 commit comments