Skip to content

Commit 034866c

Browse files
Xu Kuohaixukuohai
Xu Kuohai
authored andcommitted
bpf, arm64: bpf trampoline for arm64
Add bpf trampoline support for arm64. Most of the logic is the same as x86. fentry before bpf trampoline hooked: mov x9, x30 nop fentry after bpf trampoline hooked: mov x9, x30 bl <bpf_trampoline> Tested on qemu, result: raspberrypi#18 bpf_tcp_ca:OK raspberrypi#51 dummy_st_ops:OK raspberrypi#55 fentry_fexit:OK raspberrypi#56 fentry_test:OK raspberrypi#57 fexit_bpf2bpf:OK raspberrypi#58 fexit_sleep:OK raspberrypi#59 fexit_stress:OK raspberrypi#60 fexit_test:OK raspberrypi#67 get_func_args_test:OK raspberrypi#68 get_func_ip_test:OK raspberrypi#101 modify_return:OK raspberrypi#233 xdp_bpf2bpf:OK Signed-off-by: Xu Kuohai <[email protected]> Acked-by: Song Liu <[email protected]>
1 parent d4bcdb6 commit 034866c

File tree

2 files changed

+350
-1
lines changed

2 files changed

+350
-1
lines changed

arch/arm64/net/bpf_jit.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@
9090
/* Rt = Rn[0]; Rt2 = Rn[8]; Rn += 16; */
9191
#define A64_POP(Rt, Rt2, Rn) A64_LS_PAIR(Rt, Rt2, Rn, 16, LOAD, POST_INDEX)
9292

93+
/* Rn[imm] = Xt1; Rn[imm + 8] = Xt2 */
94+
#define A64_STP(Xt1, Xt2, Xn, imm) \
95+
A64_LS_PAIR(Xt1, Xt2, Xn, imm, STORE, SIGNED_OFFSET)
96+
/* Xt1 = Rn[imm]; Xt2 = Rn[imm + 8] */
97+
#define A64_LDP(Xt1, Xt2, Xn, imm) \
98+
A64_LS_PAIR(Xt1, Xt2, Xn, imm, LOAD, SIGNED_OFFSET)
99+
93100
/* Load/store exclusive */
94101
#define A64_SIZE(sf) \
95102
((sf) ? AARCH64_INSN_SIZE_64 : AARCH64_INSN_SIZE_32)

arch/arm64/net/bpf_jit_comp.c

Lines changed: 343 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,13 @@ static int validate_code(struct jit_ctx *ctx)
13491349
if (a64_insn == AARCH64_BREAK_FAULT)
13501350
return -1;
13511351
}
1352+
return 0;
1353+
}
1354+
1355+
static int validate_ctx(struct jit_ctx *ctx)
1356+
{
1357+
if (validate_code(ctx))
1358+
return -1;
13521359

13531360
if (WARN_ON_ONCE(ctx->exentry_idx != ctx->prog->aux->num_exentries))
13541361
return -1;
@@ -1473,7 +1480,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog)
14731480
build_epilogue(&ctx);
14741481

14751482
/* 3. Extra pass to validate JITed code. */
1476-
if (validate_code(&ctx)) {
1483+
if (validate_ctx(&ctx)) {
14771484
bpf_jit_binary_free(header);
14781485
prog = orig_prog;
14791486
goto out_off;
@@ -1544,6 +1551,341 @@ void bpf_jit_free_exec(void *addr)
15441551
return vfree(addr);
15451552
}
15461553

1554+
static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_prog *p,
1555+
int args_off, int retval_off, bool save_ret)
1556+
{
1557+
u32 *branch;
1558+
u64 enter_prog;
1559+
u64 exit_prog;
1560+
u8 tmp = bpf2a64[TMP_REG_1];
1561+
u8 r0 = bpf2a64[BPF_REG_0];
1562+
1563+
if (p->aux->sleepable) {
1564+
enter_prog = (u64)__bpf_prog_enter_sleepable;
1565+
exit_prog = (u64)__bpf_prog_exit_sleepable;
1566+
} else {
1567+
enter_prog = (u64)__bpf_prog_enter;
1568+
exit_prog = (u64)__bpf_prog_exit;
1569+
}
1570+
1571+
/* arg1: prog */
1572+
emit_addr_mov_i64(A64_R(0), (const u64)p, ctx);
1573+
/* bl __bpf_prog_enter */
1574+
emit_addr_mov_i64(tmp, enter_prog, ctx);
1575+
emit(A64_BLR(tmp), ctx);
1576+
1577+
/* if (__bpf_prog_enter(prog) == 0)
1578+
* goto skip_exec_of_prog;
1579+
*/
1580+
branch = ctx->image + ctx->idx;
1581+
emit(A64_NOP, ctx);
1582+
1583+
/* move return value to x19 */
1584+
emit(A64_MOV(1, A64_R(19), r0), ctx);
1585+
1586+
/* bl bpf_prog */
1587+
emit(A64_ADD_I(1, A64_R(0), A64_SP, args_off), ctx);
1588+
if (!p->jited)
1589+
emit_addr_mov_i64(A64_R(1), (const u64)p->insnsi, ctx);
1590+
emit_addr_mov_i64(tmp, (const u64)p->bpf_func, ctx);
1591+
emit(A64_BLR(tmp), ctx);
1592+
1593+
/* store return value */
1594+
if (save_ret)
1595+
emit(A64_STR64I(r0, A64_SP, retval_off), ctx);
1596+
1597+
if (ctx->image) {
1598+
int offset = &ctx->image[ctx->idx] - branch;
1599+
*branch = A64_CBZ(1, A64_R(0), offset);
1600+
}
1601+
1602+
/* arg1: prog */
1603+
emit_addr_mov_i64(A64_R(0), (const u64)p, ctx);
1604+
/* arg2: start time */
1605+
emit(A64_MOV(1, A64_R(1), A64_R(19)), ctx);
1606+
/* bl __bpf_prog_exit */
1607+
emit_addr_mov_i64(tmp, exit_prog, ctx);
1608+
emit(A64_BLR(tmp), ctx);
1609+
}
1610+
1611+
static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_progs *tp,
1612+
int args_off, int retval_off, u32 **branches)
1613+
{
1614+
int i;
1615+
1616+
/* The first fmod_ret program will receive a garbage return value.
1617+
* Set this to 0 to avoid confusing the program.
1618+
*/
1619+
emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx);
1620+
for (i = 0; i < tp->nr_progs; i++) {
1621+
invoke_bpf_prog(ctx, tp->progs[i], args_off, retval_off, true);
1622+
/* if (*(u64 *)(sp + retval_off) != 0)
1623+
* goto do_fexit;
1624+
*/
1625+
emit(A64_LDR64I(A64_R(10), A64_SP, retval_off), ctx);
1626+
/* Save the location of branch, and generate a nop.
1627+
* This nop will be replaced with a cbnz later.
1628+
*/
1629+
branches[i] = ctx->image + ctx->idx;
1630+
emit(A64_NOP, ctx);
1631+
}
1632+
}
1633+
1634+
static void save_args(struct jit_ctx *ctx, int args_off, int nargs)
1635+
{
1636+
int i;
1637+
1638+
for (i = 0; i < nargs; i++) {
1639+
emit(A64_STR64I(i, A64_SP, args_off), ctx);
1640+
args_off += 8;
1641+
}
1642+
}
1643+
1644+
static void restore_args(struct jit_ctx *ctx, int args_off, int nargs)
1645+
{
1646+
int i;
1647+
1648+
for (i = 0; i < nargs; i++) {
1649+
emit(A64_LDR64I(i, A64_SP, args_off), ctx);
1650+
args_off += 8;
1651+
}
1652+
}
1653+
1654+
/*
1655+
* Based on the x86's implementation of arch_prepare_bpf_trampoline().
1656+
*
1657+
* We rely on DYNAMIC_FTRACE_WITH_REGS to set return address and nop
1658+
* for fentry.
1659+
*
1660+
* fentry before bpf trampoline hooked:
1661+
* mov x9, x30
1662+
* nop
1663+
*
1664+
* fentry after bpf trampoline hooked:
1665+
* mov x9, x30
1666+
* bl <bpf_trampoline>
1667+
*
1668+
*/
1669+
static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
1670+
struct bpf_tramp_progs *tprogs, void *orig_call,
1671+
int nargs, u32 flags)
1672+
{
1673+
int i;
1674+
int stack_size;
1675+
int retaddr_off;
1676+
int regs_off;
1677+
int retval_off;
1678+
int args_off;
1679+
int nargs_off;
1680+
int ip_off;
1681+
struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY];
1682+
struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT];
1683+
struct bpf_tramp_progs *fmod_ret = &tprogs[BPF_TRAMP_MODIFY_RETURN];
1684+
bool save_ret;
1685+
u32 **branches = NULL;
1686+
1687+
/*
1688+
* trampoline stack layout:
1689+
* [ parent ip ]
1690+
* [ FP ]
1691+
* SP + retaddr_off [ self ip ]
1692+
* FP [ FP ]
1693+
*
1694+
* sp + regs_off [ x19 ] callee-saved regs, currently
1695+
* only x19 is used
1696+
*
1697+
* SP + retval_off [ return value ] BPF_TRAMP_F_CALL_ORIG or
1698+
* BPF_TRAMP_F_RET_FENTRY_RET flags
1699+
*
1700+
* [ argN ]
1701+
* [ ... ]
1702+
* sp + args_off [ arg1 ]
1703+
*
1704+
* SP + nargs_off [ args count ]
1705+
*
1706+
* SP + ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
1707+
*/
1708+
1709+
stack_size = 0;
1710+
ip_off = stack_size;
1711+
1712+
/* room for IP address argument */
1713+
if (flags & BPF_TRAMP_F_IP_ARG)
1714+
stack_size += 8;
1715+
1716+
nargs_off = stack_size;
1717+
/* room for args count */
1718+
stack_size += 8;
1719+
1720+
args_off = stack_size;
1721+
/* room for args */
1722+
stack_size += nargs * 8;
1723+
1724+
/* room for return value */
1725+
retval_off = stack_size;
1726+
save_ret = flags & (BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_RET_FENTRY_RET);
1727+
if (save_ret)
1728+
stack_size += 8;
1729+
1730+
/* room for callee-saved registers, currently only x19 is used */
1731+
regs_off = stack_size;
1732+
stack_size += 8;
1733+
1734+
retaddr_off = stack_size + 8;
1735+
1736+
if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
1737+
emit(A64_BTI_C, ctx);
1738+
1739+
/* frame for parent function */
1740+
emit(A64_PUSH(A64_FP, A64_R(9), A64_SP), ctx);
1741+
emit(A64_MOV(1, A64_FP, A64_SP), ctx);
1742+
1743+
/* frame for patched function */
1744+
emit(A64_PUSH(A64_FP, A64_LR, A64_SP), ctx);
1745+
emit(A64_MOV(1, A64_FP, A64_SP), ctx);
1746+
1747+
/* allocate stack space */
1748+
emit(A64_SUB_I(1, A64_SP, A64_SP, stack_size), ctx);
1749+
1750+
if (flags & BPF_TRAMP_F_IP_ARG) {
1751+
/* save ip address of the traced function */
1752+
emit_addr_mov_i64(A64_R(10), (const u64)orig_call, ctx);
1753+
emit(A64_STR64I(A64_R(10), A64_SP, ip_off), ctx);
1754+
}
1755+
1756+
/* save args count*/
1757+
emit(A64_MOVZ(1, A64_R(10), nargs, 0), ctx);
1758+
emit(A64_STR64I(A64_R(10), A64_SP, nargs_off), ctx);
1759+
1760+
/* save args */
1761+
save_args(ctx, args_off, nargs);
1762+
1763+
/* save callee saved registers */
1764+
emit(A64_STR64I(A64_R(19), A64_SP, regs_off), ctx);
1765+
1766+
if (flags & BPF_TRAMP_F_CALL_ORIG) {
1767+
emit_addr_mov_i64(A64_R(0), (const u64)im, ctx);
1768+
emit_addr_mov_i64(A64_R(10), (const u64)__bpf_tramp_enter, ctx);
1769+
emit(A64_BLR(A64_R(10)), ctx);
1770+
}
1771+
1772+
for (i = 0; i < fentry->nr_progs; i++)
1773+
invoke_bpf_prog(ctx, fentry->progs[i], args_off, retval_off,
1774+
flags & BPF_TRAMP_F_RET_FENTRY_RET);
1775+
1776+
if (fmod_ret->nr_progs) {
1777+
branches = kcalloc(fmod_ret->nr_progs, sizeof(u32 *),
1778+
GFP_KERNEL);
1779+
if (!branches)
1780+
return -ENOMEM;
1781+
1782+
invoke_bpf_mod_ret(ctx, fmod_ret, args_off, retval_off,
1783+
branches);
1784+
}
1785+
1786+
if (flags & BPF_TRAMP_F_CALL_ORIG) {
1787+
restore_args(ctx, args_off, nargs);
1788+
emit(A64_LDR64I(A64_R(10), A64_SP, retaddr_off), ctx);
1789+
/* call original func */
1790+
emit(A64_BLR(A64_R(10)), ctx);
1791+
/* store return value */
1792+
emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx);
1793+
/* reserve a nop */
1794+
im->ip_after_call = ctx->image + ctx->idx;
1795+
emit(A64_NOP, ctx);
1796+
}
1797+
1798+
/* update the branches saved in invoke_bpf_mod_ret with cbnz */
1799+
for (i = 0; i < fmod_ret->nr_progs && ctx->image != NULL; i++) {
1800+
int offset = &ctx->image[ctx->idx] - branches[i];
1801+
*branches[i] = A64_CBNZ(1, A64_R(10), offset);
1802+
}
1803+
1804+
for (i = 0; i < fexit->nr_progs; i++)
1805+
invoke_bpf_prog(ctx, fexit->progs[i], args_off, retval_off,
1806+
false);
1807+
1808+
if (flags & BPF_TRAMP_F_RESTORE_REGS)
1809+
restore_args(ctx, args_off, nargs);
1810+
1811+
if (flags & BPF_TRAMP_F_CALL_ORIG) {
1812+
im->ip_epilogue = ctx->image + ctx->idx;
1813+
emit_addr_mov_i64(A64_R(0), (const u64)im, ctx);
1814+
emit_addr_mov_i64(A64_R(10), (const u64)__bpf_tramp_exit, ctx);
1815+
emit(A64_BLR(A64_R(10)), ctx);
1816+
}
1817+
1818+
/* restore x19 */
1819+
emit(A64_LDR64I(A64_R(19), A64_SP, regs_off), ctx);
1820+
1821+
if (save_ret)
1822+
emit(A64_LDR64I(A64_R(0), A64_SP, retval_off), ctx);
1823+
1824+
/* reset SP */
1825+
emit(A64_MOV(1, A64_SP, A64_FP), ctx);
1826+
1827+
/* pop frames */
1828+
emit(A64_POP(A64_FP, A64_LR, A64_SP), ctx);
1829+
emit(A64_POP(A64_FP, A64_R(9), A64_SP), ctx);
1830+
1831+
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
1832+
/* skip patched function, return to parent */
1833+
emit(A64_MOV(1, A64_LR, A64_R(9)), ctx);
1834+
emit(A64_RET(A64_R(9)), ctx);
1835+
} else {
1836+
/* return to patched function */
1837+
emit(A64_MOV(1, A64_R(10), A64_LR), ctx);
1838+
emit(A64_MOV(1, A64_LR, A64_R(9)), ctx);
1839+
emit(A64_RET(A64_R(10)), ctx);
1840+
}
1841+
1842+
if (ctx->image)
1843+
bpf_flush_icache(ctx->image, ctx->image + ctx->idx);
1844+
1845+
kfree(branches);
1846+
1847+
return ctx->idx;
1848+
}
1849+
1850+
int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image,
1851+
void *image_end, const struct btf_func_model *m,
1852+
u32 flags, struct bpf_tramp_progs *tprogs,
1853+
void *orig_call)
1854+
{
1855+
int ret;
1856+
int nargs = m->nr_args;
1857+
int max_insns = ((long)image_end - (long)image) / AARCH64_INSN_SIZE;
1858+
struct jit_ctx ctx = {
1859+
.image = NULL,
1860+
.idx = 0
1861+
};
1862+
1863+
/* the first 8 arguments are passed by registers */
1864+
if (nargs > 8)
1865+
return -ENOTSUPP;
1866+
1867+
ret = prepare_trampoline(&ctx, im, tprogs, orig_call, nargs, flags);
1868+
if (ret < 0)
1869+
return ret;
1870+
1871+
if (ret > max_insns)
1872+
return -EFBIG;
1873+
1874+
ctx.image = image;
1875+
ctx.idx = 0;
1876+
1877+
jit_fill_hole(image, (unsigned int)(image_end - image));
1878+
ret = prepare_trampoline(&ctx, im, tprogs, orig_call, nargs, flags);
1879+
1880+
if (ret > 0 && validate_code(&ctx) < 0)
1881+
ret = -EINVAL;
1882+
1883+
if (ret > 0)
1884+
ret *= AARCH64_INSN_SIZE;
1885+
1886+
return ret;
1887+
}
1888+
15471889
static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip,
15481890
void *addr, u32 *insn)
15491891
{

0 commit comments

Comments
 (0)