// SPDX-License-Identifier: GPL-2.0-only1/*2* arch/arm64/kernel/ftrace.c3*4* Copyright (C) 2013 Linaro Limited5* Author: AKASHI Takahiro <[email protected]>6*/78#include <linux/ftrace.h>9#include <linux/module.h>10#include <linux/swab.h>11#include <linux/uaccess.h>1213#include <asm/cacheflush.h>14#include <asm/debug-monitors.h>15#include <asm/ftrace.h>16#include <asm/insn.h>17#include <asm/text-patching.h>1819#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS20struct fregs_offset {21const char *name;22int offset;23};2425#define FREGS_OFFSET(n, field) \26{ \27.name = n, \28.offset = offsetof(struct __arch_ftrace_regs, field), \29}3031static const struct fregs_offset fregs_offsets[] = {32FREGS_OFFSET("x0", regs[0]),33FREGS_OFFSET("x1", regs[1]),34FREGS_OFFSET("x2", regs[2]),35FREGS_OFFSET("x3", regs[3]),36FREGS_OFFSET("x4", regs[4]),37FREGS_OFFSET("x5", regs[5]),38FREGS_OFFSET("x6", regs[6]),39FREGS_OFFSET("x7", regs[7]),40FREGS_OFFSET("x8", regs[8]),4142FREGS_OFFSET("x29", fp),43FREGS_OFFSET("x30", lr),44FREGS_OFFSET("lr", lr),4546FREGS_OFFSET("sp", sp),47FREGS_OFFSET("pc", pc),48};4950int ftrace_regs_query_register_offset(const char *name)51{52for (int i = 0; i < ARRAY_SIZE(fregs_offsets); i++) {53const struct fregs_offset *roff = &fregs_offsets[i];54if (!strcmp(roff->name, name))55return roff->offset;56}5758return -EINVAL;59}60#endif6162unsigned long ftrace_call_adjust(unsigned long addr)63{64/*65* When using mcount, addr is the address of the mcount call66* instruction, and no adjustment is necessary.67*/68if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_ARGS))69return addr;7071/*72* When using patchable-function-entry without pre-function NOPS, addr73* is the address of the first NOP after the function entry point.74*75* The compiler has either generated:76*77* addr+00: func: NOP // To be patched to MOV X9, LR78* addr+04: NOP // To be patched to BL <caller>79*80* Or:81*82* addr-04: BTI C83* addr+00: func: NOP // To be patched to MOV X9, LR84* addr+04: NOP // To be patched to BL <caller>85*86* We must adjust addr to the address of the NOP which will be patched87* to `BL <caller>`, which is at `addr + 4` bytes in either case.88*89*/90if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))91return addr + AARCH64_INSN_SIZE;9293/*94* When using patchable-function-entry with pre-function NOPs, addr is95* the address of the first pre-function NOP.96*97* Starting from an 8-byte aligned base, the compiler has either98* generated:99*100* addr+00: NOP // Literal (first 32 bits)101* addr+04: NOP // Literal (last 32 bits)102* addr+08: func: NOP // To be patched to MOV X9, LR103* addr+12: NOP // To be patched to BL <caller>104*105* Or:106*107* addr+00: NOP // Literal (first 32 bits)108* addr+04: NOP // Literal (last 32 bits)109* addr+08: func: BTI C110* addr+12: NOP // To be patched to MOV X9, LR111* addr+16: NOP // To be patched to BL <caller>112*113* We must adjust addr to the address of the NOP which will be patched114* to `BL <caller>`, which is at either addr+12 or addr+16 depending on115* whether there is a BTI.116*/117118if (!IS_ALIGNED(addr, sizeof(unsigned long))) {119WARN_RATELIMIT(1, "Misaligned patch-site %pS\n",120(void *)(addr + 8));121return 0;122}123124/* Skip the NOPs placed before the function entry point */125addr += 2 * AARCH64_INSN_SIZE;126127/* Skip any BTI */128if (IS_ENABLED(CONFIG_ARM64_BTI_KERNEL)) {129u32 insn = le32_to_cpu(*(__le32 *)addr);130131if (aarch64_insn_is_bti(insn)) {132addr += AARCH64_INSN_SIZE;133} else if (insn != aarch64_insn_gen_nop()) {134WARN_RATELIMIT(1, "unexpected insn in patch-site %pS: 0x%08x\n",135(void *)addr, insn);136}137}138139/* Skip the first NOP after function entry */140addr += AARCH64_INSN_SIZE;141142return addr;143}144145/* Convert fentry_ip to the symbol address without kallsyms */146unsigned long arch_ftrace_get_symaddr(unsigned long fentry_ip)147{148u32 insn;149150/*151* When using patchable-function-entry without pre-function NOPS, ftrace152* entry is the address of the first NOP after the function entry point.153*154* The compiler has either generated:155*156* func+00: func: NOP // To be patched to MOV X9, LR157* func+04: NOP // To be patched to BL <caller>158*159* Or:160*161* func-04: BTI C162* func+00: func: NOP // To be patched to MOV X9, LR163* func+04: NOP // To be patched to BL <caller>164*165* The fentry_ip is the address of `BL <caller>` which is at `func + 4`166* bytes in either case.167*/168if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))169return fentry_ip - AARCH64_INSN_SIZE;170171/*172* When using patchable-function-entry with pre-function NOPs, BTI is173* a bit different.174*175* func+00: func: NOP // To be patched to MOV X9, LR176* func+04: NOP // To be patched to BL <caller>177*178* Or:179*180* func+00: func: BTI C181* func+04: NOP // To be patched to MOV X9, LR182* func+08: NOP // To be patched to BL <caller>183*184* The fentry_ip is the address of `BL <caller>` which is at either185* `func + 4` or `func + 8` depends on whether there is a BTI.186*/187188/* If there is no BTI, the func address should be one instruction before. */189if (!IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))190return fentry_ip - AARCH64_INSN_SIZE;191192/* We want to be extra safe in case entry ip is on the page edge,193* but otherwise we need to avoid get_kernel_nofault()'s overhead.194*/195if ((fentry_ip & ~PAGE_MASK) < AARCH64_INSN_SIZE * 2) {196if (get_kernel_nofault(insn, (u32 *)(fentry_ip - AARCH64_INSN_SIZE * 2)))197return 0;198} else {199insn = *(u32 *)(fentry_ip - AARCH64_INSN_SIZE * 2);200}201202if (aarch64_insn_is_bti(le32_to_cpu((__le32)insn)))203return fentry_ip - AARCH64_INSN_SIZE * 2;204205return fentry_ip - AARCH64_INSN_SIZE;206}207208/*209* Replace a single instruction, which may be a branch or NOP.210* If @validate == true, a replaced instruction is checked against 'old'.211*/212static int ftrace_modify_code(unsigned long pc, u32 old, u32 new,213bool validate)214{215u32 replaced;216217/*218* Note:219* We are paranoid about modifying text, as if a bug were to happen, it220* could cause us to read or write to someplace that could cause harm.221* Carefully read and modify the code with aarch64_insn_*() which uses222* probe_kernel_*(), and make sure what we read is what we expected it223* to be before modifying it.224*/225if (validate) {226if (aarch64_insn_read((void *)pc, &replaced))227return -EFAULT;228229if (replaced != old)230return -EINVAL;231}232if (aarch64_insn_patch_text_nosync((void *)pc, new))233return -EPERM;234235return 0;236}237238/*239* Replace tracer function in ftrace_caller()240*/241int ftrace_update_ftrace_func(ftrace_func_t func)242{243unsigned long pc;244u32 new;245246/*247* When using CALL_OPS, the function to call is associated with the248* call site, and we don't have a global function pointer to update.249*/250if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS))251return 0;252253pc = (unsigned long)ftrace_call;254new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func,255AARCH64_INSN_BRANCH_LINK);256257return ftrace_modify_code(pc, 0, new, false);258}259260static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)261{262#ifdef CONFIG_MODULES263struct plt_entry *plt = NULL;264265if (within_module_mem_type(addr, mod, MOD_INIT_TEXT))266plt = mod->arch.init_ftrace_trampolines;267else if (within_module_mem_type(addr, mod, MOD_TEXT))268plt = mod->arch.ftrace_trampolines;269else270return NULL;271272return &plt[FTRACE_PLT_IDX];273#else274return NULL;275#endif276}277278static bool reachable_by_bl(unsigned long addr, unsigned long pc)279{280long offset = (long)addr - (long)pc;281282return offset >= -SZ_128M && offset < SZ_128M;283}284285/*286* Find the address the callsite must branch to in order to reach '*addr'.287*288* Due to the limited range of 'BL' instructions, modules may be placed too far289* away to branch directly and must use a PLT.290*291* Returns true when '*addr' contains a reachable target address, or has been292* modified to contain a PLT address. Returns false otherwise.293*/294static bool ftrace_find_callable_addr(struct dyn_ftrace *rec,295struct module *mod,296unsigned long *addr)297{298unsigned long pc = rec->ip;299struct plt_entry *plt;300301/*302* If a custom trampoline is unreachable, rely on the ftrace_caller303* trampoline which knows how to indirectly reach that trampoline304* through ops->direct_call.305*/306if (*addr != FTRACE_ADDR && !reachable_by_bl(*addr, pc))307*addr = FTRACE_ADDR;308309/*310* When the target is within range of the 'BL' instruction, use 'addr'311* as-is and branch to that directly.312*/313if (reachable_by_bl(*addr, pc))314return true;315316/*317* When the target is outside of the range of a 'BL' instruction, we318* must use a PLT to reach it. We can only place PLTs for modules, and319* only when module PLT support is built-in.320*/321if (!IS_ENABLED(CONFIG_MODULES))322return false;323324/*325* 'mod' is only set at module load time, but if we end up326* dealing with an out-of-range condition, we can assume it327* is due to a module being loaded far away from the kernel.328*329* NOTE: __module_text_address() must be called within a RCU read330* section, but we can rely on ftrace_lock to ensure that 'mod'331* retains its validity throughout the remainder of this code.332*/333if (!mod) {334guard(rcu)();335mod = __module_text_address(pc);336}337338if (WARN_ON(!mod))339return false;340341plt = get_ftrace_plt(mod, pc);342if (!plt) {343pr_err("ftrace: no module PLT for %ps\n", (void *)*addr);344return false;345}346347*addr = (unsigned long)plt;348return true;349}350351#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS352static const struct ftrace_ops *arm64_rec_get_ops(struct dyn_ftrace *rec)353{354const struct ftrace_ops *ops = NULL;355356if (rec->flags & FTRACE_FL_CALL_OPS_EN) {357ops = ftrace_find_unique_ops(rec);358WARN_ON_ONCE(!ops);359}360361if (!ops)362ops = &ftrace_list_ops;363364return ops;365}366367static int ftrace_rec_set_ops(const struct dyn_ftrace *rec,368const struct ftrace_ops *ops)369{370unsigned long literal = ALIGN_DOWN(rec->ip - 12, 8);371return aarch64_insn_write_literal_u64((void *)literal,372(unsigned long)ops);373}374375static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec)376{377return ftrace_rec_set_ops(rec, &ftrace_nop_ops);378}379380static int ftrace_rec_update_ops(struct dyn_ftrace *rec)381{382return ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec));383}384#else385static int ftrace_rec_set_nop_ops(struct dyn_ftrace *rec) { return 0; }386static int ftrace_rec_update_ops(struct dyn_ftrace *rec) { return 0; }387#endif388389/*390* Turn on the call to ftrace_caller() in instrumented function391*/392int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)393{394unsigned long pc = rec->ip;395u32 old, new;396int ret;397398ret = ftrace_rec_update_ops(rec);399if (ret)400return ret;401402if (!ftrace_find_callable_addr(rec, NULL, &addr))403return -EINVAL;404405old = aarch64_insn_gen_nop();406new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);407408return ftrace_modify_code(pc, old, new, true);409}410411#ifdef CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS412int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,413unsigned long addr)414{415unsigned long pc = rec->ip;416u32 old, new;417int ret;418419ret = ftrace_rec_set_ops(rec, arm64_rec_get_ops(rec));420if (ret)421return ret;422423if (!ftrace_find_callable_addr(rec, NULL, &old_addr))424return -EINVAL;425if (!ftrace_find_callable_addr(rec, NULL, &addr))426return -EINVAL;427428old = aarch64_insn_gen_branch_imm(pc, old_addr,429AARCH64_INSN_BRANCH_LINK);430new = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);431432return ftrace_modify_code(pc, old, new, true);433}434#endif435436#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS437/*438* The compiler has inserted two NOPs before the regular function prologue.439* All instrumented functions follow the AAPCS, so x0-x8 and x19-x30 are live,440* and x9-x18 are free for our use.441*442* At runtime we want to be able to swing a single NOP <-> BL to enable or443* disable the ftrace call. The BL requires us to save the original LR value,444* so here we insert a <MOV X9, LR> over the first NOP so the instructions445* before the regular prologue are:446*447* | Compiled | Disabled | Enabled |448* +----------+------------+------------+449* | NOP | MOV X9, LR | MOV X9, LR |450* | NOP | NOP | BL <entry> |451*452* The LR value will be recovered by ftrace_caller, and restored into LR453* before returning to the regular function prologue. When a function is not454* being traced, the MOV is not harmful given x9 is not live per the AAPCS.455*456* Note: ftrace_process_locs() has pre-adjusted rec->ip to be the address of457* the BL.458*/459int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)460{461unsigned long pc = rec->ip - AARCH64_INSN_SIZE;462u32 old, new;463int ret;464465ret = ftrace_rec_set_nop_ops(rec);466if (ret)467return ret;468469old = aarch64_insn_gen_nop();470new = aarch64_insn_gen_move_reg(AARCH64_INSN_REG_9,471AARCH64_INSN_REG_LR,472AARCH64_INSN_VARIANT_64BIT);473return ftrace_modify_code(pc, old, new, true);474}475#endif476477/*478* Turn off the call to ftrace_caller() in instrumented function479*/480int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,481unsigned long addr)482{483unsigned long pc = rec->ip;484u32 old = 0, new;485int ret;486487new = aarch64_insn_gen_nop();488489ret = ftrace_rec_set_nop_ops(rec);490if (ret)491return ret;492493/*494* When using mcount, callsites in modules may have been initalized to495* call an arbitrary module PLT (which redirects to the _mcount stub)496* rather than the ftrace PLT we'll use at runtime (which redirects to497* the ftrace trampoline). We can ignore the old PLT when initializing498* the callsite.499*500* Note: 'mod' is only set at module load time.501*/502if (!IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_ARGS) && mod)503return aarch64_insn_patch_text_nosync((void *)pc, new);504505if (!ftrace_find_callable_addr(rec, mod, &addr))506return -EINVAL;507508old = aarch64_insn_gen_branch_imm(pc, addr, AARCH64_INSN_BRANCH_LINK);509510return ftrace_modify_code(pc, old, new, true);511}512513void arch_ftrace_update_code(int command)514{515command |= FTRACE_MAY_SLEEP;516ftrace_modify_all_code(command);517}518519#ifdef CONFIG_FUNCTION_GRAPH_TRACER520/*521* function_graph tracer expects ftrace_return_to_handler() to be called522* on the way back to parent. For this purpose, this function is called523* in _mcount() or ftrace_caller() to replace return address (*parent) on524* the call stack to return_to_handler.525*/526void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,527unsigned long frame_pointer)528{529unsigned long return_hooker = (unsigned long)&return_to_handler;530unsigned long old;531532if (unlikely(atomic_read(¤t->tracing_graph_pause)))533return;534535/*536* Note:537* No protection against faulting at *parent, which may be seen538* on other archs. It's unlikely on AArch64.539*/540old = *parent;541542if (!function_graph_enter(old, self_addr, frame_pointer,543(void *)frame_pointer)) {544*parent = return_hooker;545}546}547548#ifdef CONFIG_DYNAMIC_FTRACE_WITH_ARGS549void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,550struct ftrace_ops *op, struct ftrace_regs *fregs)551{552unsigned long return_hooker = (unsigned long)&return_to_handler;553unsigned long frame_pointer = arch_ftrace_regs(fregs)->fp;554unsigned long *parent = &arch_ftrace_regs(fregs)->lr;555unsigned long old;556557if (unlikely(atomic_read(¤t->tracing_graph_pause)))558return;559560old = *parent;561562if (!function_graph_enter_regs(old, ip, frame_pointer,563(void *)frame_pointer, fregs)) {564*parent = return_hooker;565}566}567#else568/*569* Turn on/off the call to ftrace_graph_caller() in ftrace_caller()570* depending on @enable.571*/572static int ftrace_modify_graph_caller(bool enable)573{574unsigned long pc = (unsigned long)&ftrace_graph_call;575u32 branch, nop;576577branch = aarch64_insn_gen_branch_imm(pc,578(unsigned long)ftrace_graph_caller,579AARCH64_INSN_BRANCH_NOLINK);580nop = aarch64_insn_gen_nop();581582if (enable)583return ftrace_modify_code(pc, nop, branch, true);584else585return ftrace_modify_code(pc, branch, nop, true);586}587588int ftrace_enable_ftrace_graph_caller(void)589{590return ftrace_modify_graph_caller(true);591}592593int ftrace_disable_ftrace_graph_caller(void)594{595return ftrace_modify_graph_caller(false);596}597#endif /* CONFIG_DYNAMIC_FTRACE_WITH_ARGS */598#endif /* CONFIG_FUNCTION_GRAPH_TRACER */599600601