ARM64 hw watchpoint、breakpoint注册与注销

ARM64 hw watchpoint、breakpoint注册与注销

 write_wb_reg()里的switch macro展开

4.19\arch\arm64\kernel\Hw_breakpoint.c

复制代码
static void write_wb_reg(int reg, int n, u64 val)
{

switch (reg + n) {

GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_BVR, AARCH64_DBG_REG_NAME_BVR, val);

GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_BCR, AARCH64_DBG_REG_NAME_BCR, val);

GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_WVR, AARCH64_DBG_REG_NAME_WVR, val);

GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_WCR, AARCH64_DBG_REG_NAME_WCR, val);

default:

pr_warning("attempt to write to unknown breakpoint register %d\n", n);

}

isb();

}

above switch expanding as below:

switch (reg + n) {

/* wvr(breakpoint value register) */

case (0): \

do {\

write_sysreg(val, dbgbvr0_el1);\

} while (0); \

break;

case (1): \

do {\

write_sysreg(val, dbgbvr1_el1);\

} while (0); \

break;

case (15): \

do {\

write_sysreg(val, dbgbvr15_el1);\

} while (0); \

break;

/* wvr(breakpoint ctrl register) */

case (16 + 0): \

do {\

write_sysreg(VAL, dbgbcr0_el1);\

} while (0); \

break

case (16 + 1): \

do {\

write_sysreg(VAL, dbgbcr1_el1);\

} while (0); \

break

case (16 + 15): \

do {\

write_sysreg(VAL, dbgbcr15_el1);\

} while (0); \

break

/* wvr(watchpoint value register) */

case (32 + 0): \

do {\

write_sysreg(VAL, dbgwvr0_el1);\

} while (0); \

break

case (32 + 1): \

do {\

write_sysreg(VAL, dbgwvr1_el1);\

} while (0); \

break

case (32 + 15): \

do {\

write_sysreg(VAL, dbgwvr15_el1);\

} while (0); \

break

/* wvr(watchpoint ctrl register) */

case (48 + 0): \

do {\

write_sysreg(VAL, dbgwcr0_el1);\

} while (0); \

break

case (48 + 1): \

do {\

write_sysreg(VAL, dbgwcr1_el1);\

} while (0); \

break

case (48 + 15): \

do {\

write_sysreg(VAL, dbgwcr15_el1);\

} while (0); \

break

}
复制代码

 bvr/bcr/wvr/wcr寄存器分别为breakpoint value register/breakpoint ctrl register/watchpoint value register/watchpoint ctrl register

这些寄存器描述见下面链接:

https://developer.arm.com/documentation/ddi0500/e/debug/memory-mapped-register-summary

注册watchpoint callstack

设置watchpoint、breakpoint需要设置CPU寄存器,设置寄存器是通过上述write_wb_reg()来设置的,它的示例设置callstack如下:

复制代码
[   28.809660] CPU: 1 PID: 34 Comm: kworker/1:1 Tainted: P           O      4.19.116+ #49
[   28.817581] Hardware name: test_mach (DT)
[   28.821261] Workqueue: events watch_point_delayed_work_func
[   28.826841] Call trace:
[   28.829293]  dump_backtrace+0x0/0x4
[   28.832786]  dump_stack+0xf4/0x134
[   28.836192]  write_wb_reg+0x3c/0x284
[   28.839769]  hw_breakpoint_control+0x18c/0x264
[   28.844216]  hw_breakpoint_add+0x80/0x8c
[   28.848143]  event_sched_in+0x44c/0x718
[   28.851981]  group_sched_in+0x6c/0x218
[   28.855732]  pinned_sched_in+0x158/0x264
[   28.859656]  visit_groups_merge+0x140/0x1fc
[   28.863842]  ctx_sched_in+0x1a4/0x1ec
[   28.867507]  ctx_resched+0xdc/0x1e4
[   28.870998]  __perf_event_enable+0x1d0/0x2c4
[   28.875271]  event_function.9346+0x128/0x224
[   28.879543]  remote_function+0x74/0xb4
[   28.883297]  generic_exec_single+0x7c/0x1a4
[   28.887483]  smp_call_function_single+0xc0/0x1e8
[   28.892104]  event_function_call+0x84/0x274
[   28.896289]  perf_event_enable+0xec/0x158
[   28.900301]  _register_wp_bp+0x408/0x4b8
[   28.904228]  watch_point_delayed_work_func+0x68/0xcc
[   28.909197]  process_one_work+0x3c0/0x670
[   28.913210]  worker_thread+0x32c/0x6d0
[   28.916962]  kthread+0x130/0x140
[   28.920194]  ret_from_fork+0x10/0x18
复制代码

 

 

arm64 CPU支持的watchpoint、breakpoint max num

4.19\arch\arm64\include\asm\Hw_breakpoint.h

复制代码
/* Determine number of BRP registers available. */
static inline int get_num_brps(void)

{

u64 dfr0 = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);

return 1 +

cpuid_feature_extract_unsigned_field(dfr0,

ID_AA64DFR0_BRPS_SHIFT);

}

/* Determine number of WRP registers available. */

static inline int get_num_wrps(void)

{

u64 dfr0 = read_sanitised_ftr_reg(SYS_ID_AA64DFR0_EL1);

return 1 +

cpuid_feature_extract_unsigned_field(dfr0,

ID_AA64DFR0_WRPS_SHIFT);

}
复制代码

确定CPU支持的watchpoint、breakpoint max num: 

arch/arm64/kernel/hw_breakpoint.c

 

复制代码
static int __init arch_hw_breakpoint_init(void)
{
    int ret;

    core_num_brps = get_num_brps();
    core_num_wrps = get_num_wrps();

    pr_info("found %d breakpoint and %d watchpoint registers.\n",
        core_num_brps, core_num_wrps);
复制代码

 比如我这边平台CPU支持的max watchpoint、breakpoint num分别为4、6:

[    1.399866] hw-breakpoint: found 6 breakpoint and 4 watchpoint registers.

 

以watchpoint为例,如果当前已经注册了4个,如果再继续注册,则会注册fail,返回-ENOSPC(-28)

arch/arm64/kernel/hw_breakpoint.c

复制代码
static int hw_breakpoint_control(struct perf_event *bp,
                 enum hw_breakpoint_ops ops)
{
    struct arch_hw_breakpoint *info = counter_arch_bp(bp);
    struct perf_event **slots;
    struct debug_info *debug_info = &current->thread.debug;
    int i, max_slots, ctrl_reg, val_reg, reg_enable;
    enum dbg_active_el dbg_el = debug_exception_level(info->ctrl.privilege);
    u32 ctrl;

    if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
        /* Breakpoint */
        ctrl_reg = AARCH64_DBG_REG_BCR;
        val_reg = AARCH64_DBG_REG_BVR;
        slots = this_cpu_ptr(bp_on_reg);
        max_slots = core_num_brps;
        reg_enable = !debug_info->bps_disabled;
    } else {
        /* Watchpoint */
        ctrl_reg = AARCH64_DBG_REG_WCR;
        val_reg = AARCH64_DBG_REG_WVR;
        slots = this_cpu_ptr(wp_on_reg);
        max_slots = core_num_wrps;
        reg_enable = !debug_info->wps_disabled;
    }

    i = hw_breakpoint_slot_setup(slots, max_slots, bp, ops);
复制代码

 

复制代码
static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots,
                    struct perf_event *bp,
                    enum hw_breakpoint_ops ops)
{
    int i;
    struct perf_event **slot;

    for (i = 0; i < max_slots; ++i) {
        slot = &slots[i];
        switch (ops) {
        case HW_BREAKPOINT_INSTALL:
            if (!*slot) {
                *slot = bp;
                return i;
            }
            break;
        case HW_BREAKPOINT_UNINSTALL:
            if (*slot == bp) {
                *slot = NULL;
                return i;
            }
            break;
        case HW_BREAKPOINT_RESTORE:
            if (*slot == bp)
                return i;
            break;
        default:
            pr_warn_once("Unhandled hw breakpoint ops %d\n", ops);
            return -EINVAL;
        }
    }
    return -ENOSPC;
}
复制代码

 

注销watch point callstack

注销一个watchpoint callstack如下,call到hw_breakpoint_control()将ctrl reg设置为0,ops为HW_BREAKPOINT_UNINSTALL:

复制代码
复制代码
[   49.043918] hw-breakpoint: ops: 1, reg_enable: 1.
[   49.043925] CPU: 0 PID: 3237 Comm: mali-cmar-backe Tainted: P           O      4.19.116+ #19
[   49.043927] Hardware name: xxx_mach (DT)
[   49.043931] Call trace:
[   49.043942]  dump_backtrace+0x0/0x4
[   49.043949]  dump_stack+0xf4/0x134
[   49.043957]  hw_breakpoint_control+0x184/0x29c
[   49.043963]  hw_breakpoint_del+0x20/0x2c
[   49.043968]  event_sched_out+0xe0/0x344
[   49.043973]  __perf_remove_from_context+0xfc/0x16c
[   49.043977]  event_function.9234+0x128/0x224
[   49.043981]  remote_function+0x74/0xb4
[   49.043988]  generic_exec_single+0x7c/0x1a4
[   49.043992]  smp_call_function_single+0xc0/0x1e8
[   49.043996]  event_function_call+0x84/0x274
[   49.044000]  perf_event_release_kernel+0x1b8/0x794
[   49.044004]  unregister_wide_hw_breakpoint+0x90/0xf4
[   49.044010]  _unregister_wp_bp+0x10c/0x198
[   49.044016]  exit_files+0x1a0/0x1d8
[   49.044020]  do_exit+0x8ec/0x1420
[   49.044024]  do_group_exit+0x0/0x160
[   49.044027]  __se_sys_exit+0x0/0x20
[   49.044031]  el0_svc_common+0xb8/0x1b8
[   49.044035]  el0_svc_handler+0x74/0x90
[   49.044040]  el0_svc+0x8/0x340
复制代码
复制代码

 

 4.19\arch\arm64\kernel\Hw_breakpoint.c

复制代码
复制代码

static int hw_breakpoint_control(struct perf_event *bp,
enum hw_breakpoint_ops ops)

case HW_BREAKPOINT_UNINSTALL:
        /* Reset the control register. */
        write_wb_reg(ctrl_reg, i, 0);

        /*
         * Release the debug monitors for the correct exception
         * level.
         */
        disable_debug_monitors(dbg_el);
        break;
复制代码
复制代码

 

posted @   aspirs  阅读(1529)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示