├── hw_breakpoint_until.h ├── .vscode ├── settings.json └── compile_commands.json ├── .gitignore ├── Makefile ├── make.sh ├── hw_breakpoint_until.c ├── .clangd ├── README.md ├── hw_breakpoint_smp.c ├── ext_hw_breakpoint.h ├── hw_breakpoint_proc.c ├── doc └── 硬件断点驱动解析.md ├── hw_breakpoint_manage.c ├── LICENSE ├── .clang-format └── hw_breakpoint.c /hw_breakpoint_until.h: -------------------------------------------------------------------------------- 1 | #ifndef __HW_BREAKPOINT_UNTIL_H 2 | #define __HW_BREAKPOINT_UNTIL_H 3 | 4 | #include 5 | 6 | typedef struct iophys_info { 7 | struct list_head list; 8 | struct vm_struct area; 9 | u64 virt_addr; 10 | } iophys_info; 11 | 12 | void process_cmd_string(char *pBuf, int *pArgc, char *pArgv[]); 13 | /*iophy to virt func*/ 14 | iophys_info *get_iophys_info(u64 addr); 15 | void free_iophys_info(iophys_info *info); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "clangd.arguments": [ 3 | "--background-index", 4 | "--compile-commands-dir=.vscode", 5 | "-j=12", 6 | // "--folding-ranges", 7 | "--query-driver=/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/aarch64-linux-gnu-g*", 8 | "--clang-tidy", 9 | "--all-scopes-completion", 10 | "--completion-style=detailed", 11 | "--function-arg-placeholders", 12 | "--header-insertion=never", 13 | "--pch-storage=disk", 14 | ], 15 | "files.autoGuessEncoding": true 16 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | *.idx 55 | *.patch -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CROSS?= aarch64-none-linux-gnu- 2 | KERNEL_DIR?= your kernel dir 3 | CURRENT_PATH:= $(shell pwd) 4 | MODULE_NAME= hw_break 5 | 6 | src_dir?= $(shell pwd) 7 | export src_dir 8 | 9 | 10 | includedir:= -I$(src_dir)/include 11 | EXTRA_CFLAGS+= $(includedir) -g 12 | 13 | 14 | obj-m:= $(MODULE_NAME).o 15 | $(MODULE_NAME)-objs+= hw_breakpoint.o \ 16 | hw_breakpoint_manage.o \ 17 | hw_breakpoint_proc.o \ 18 | hw_breakpoint_smp.o \ 19 | hw_breakpoint_until.o \ 20 | 21 | 22 | all: ko 23 | # 编译驱动 24 | ko: 25 | make -C $(KERNEL_DIR) M=$(CURRENT_PATH) EXTRA_CFLAGS="$(EXTRA_CFLAGS)" CROSS_COMPILE=${CROSS} ARCH=arm64 modules 26 | 27 | 28 | clean: 29 | make -C $(KERNEL_DIR) M=$(CURRENT_PATH) clean -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### 3 | # @Author: zwf 240970521@qq.com 4 | # @Date: 2023-08-25 21:21:01 5 | # @LastEditors: zwf 240970521@qq.com 6 | # @LastEditTime: 2023-08-25 21:27:58 7 | # @FilePath: /hardware-breakpoint/make.sh 8 | # @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE 9 | ### 10 | KERNEL_DIR=/home/zwf/x3_src/kernel 11 | hotbot_key=$KERNEL_DIR/certs/hobot_fixed_signing_key.pem 12 | sign_key=$KERNEL_DIR/certs/signing_key.x509 13 | sign_tools=$KERNEL_DIR/scripts/sign-file 14 | 15 | rm -rf .vscode/compile_commands.json 16 | make clean 17 | bear make 18 | mv compile_commands.json .vscode/compile_commands.json 19 | 20 | #给驱动加签名 21 | $sign_tools sha512 $hotbot_key $sign_key ./hw_break.ko 22 | 23 | #copy到nfs目录 24 | cp ./hw_break.ko ~/x3sdb/nfs/ 25 | -------------------------------------------------------------------------------- /hw_breakpoint_until.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ext_hw_breakpoint.h" 3 | #include "hw_breakpoint_until.h" 4 | 5 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 72) 6 | #define VM_LAZY_FREE 0x02 7 | #define VM_VM_AREA 0x04 8 | #endif 9 | 10 | #ifdef HW_PROC_CMD_DEBUG 11 | static void print_cmd_params(int argc, char *argv[]) 12 | { 13 | int loop = 0; 14 | 15 | for (loop = 0; loop < argc; loop++) { 16 | pr_info("loop:%d, %s\n", loop, argv[loop]); 17 | } 18 | } 19 | #endif 20 | 21 | void process_cmd_string(char *p_buf, int *p_argc, char *p_argv[]) 22 | { 23 | int i_argc; 24 | char *p_tmp = p_buf; 25 | 26 | p_argv[0] = p_buf; 27 | i_argc = 1; 28 | 29 | while (*p_tmp) { 30 | if (' ' == *p_tmp) { 31 | *p_tmp = '\0'; 32 | p_argv[i_argc++] = p_tmp + 1; 33 | } 34 | 35 | p_tmp++; 36 | } 37 | *p_argc = i_argc; 38 | #ifdef HW_PROC_CMD_DEBUG 39 | print_cmd_params(*pArgc, pArgv); 40 | #endif 41 | } 42 | 43 | void free_iophys_info(iophys_info *info) 44 | { 45 | iophys_info *node = NULL, *next = NULL; 46 | 47 | if (info) { 48 | list_for_each_entry_safe (node, next, &info->list, list) { 49 | list_del(&node->list); 50 | kfree(node); 51 | } 52 | kfree(info); 53 | } 54 | } 55 | EXPORT_SYMBOL_GPL(free_iophys_info); 56 | 57 | iophys_info *get_iophys_info(u64 addr) 58 | { 59 | struct vmap_area *va = NULL; 60 | struct vm_struct *area = NULL; 61 | struct vm_struct *next = NULL; 62 | iophys_info *head = NULL; 63 | iophys_info *node = NULL; 64 | 65 | if (!HW_SYMS_VAL(vmap_area_lock) || !HW_SYMS_VAL(vmap_area_lock)) { 66 | pr_info("vmap_area_list or vmap_area_lock is NULL, can not get virt"); 67 | return head; 68 | } 69 | 70 | spin_lock(HW_SYMS_VAL(vmap_area_lock)); 71 | list_for_each_entry (va, HW_SYMS_VAL(vmap_area_list), list) { 72 | #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 72) 73 | if (!(va->flags & VM_VM_AREA)) { 74 | continue; 75 | } 76 | #endif 77 | if (!va) { 78 | continue; 79 | } 80 | area = va->vm; 81 | if (!area) { 82 | continue; 83 | } 84 | if (!(area->flags & VM_IOREMAP) || 85 | area->flags & VM_UNINITIALIZED) { 86 | continue; 87 | } 88 | smp_rmb(); 89 | /*If you find the I/O address, check whether the I/O address you want to query is within the I/O address range*/ 90 | next = area; 91 | while (next) { 92 | if (next->phys_addr && next->size) { 93 | /*The IO address to be queried is within its range*/ 94 | if (addr >= next->phys_addr && 95 | addr < next->phys_addr + next->size) { 96 | /*find it*/ 97 | if (head == NULL) { 98 | head = kzalloc( 99 | sizeof(iophys_info), 100 | GFP_KERNEL); 101 | if (head == NULL) { 102 | goto err; 103 | } 104 | INIT_LIST_HEAD(&head->list); 105 | head->area = *next; 106 | head->virt_addr = 107 | (u64)next->addr + addr - 108 | next->phys_addr; 109 | } 110 | node = kzalloc(sizeof(iophys_info), 111 | GFP_KERNEL); 112 | if (node == NULL) { 113 | goto free; 114 | } 115 | INIT_LIST_HEAD(&node->list); 116 | node->area = *next; 117 | node->virt_addr = (u64)next->addr + 118 | addr - 119 | next->phys_addr; 120 | list_add_tail(&node->list, &head->list); 121 | } 122 | } 123 | next = next->next; 124 | if (next == area) { 125 | break; 126 | } 127 | } 128 | } 129 | spin_unlock(HW_SYMS_VAL(vmap_area_lock)); 130 | 131 | return head; 132 | 133 | free: 134 | free_iophys_info(head); 135 | err: 136 | spin_unlock(HW_SYMS_VAL(vmap_area_lock)); 137 | return NULL; 138 | } 139 | EXPORT_SYMBOL_GPL(get_iophys_info); 140 | -------------------------------------------------------------------------------- /.clangd: -------------------------------------------------------------------------------- 1 | Diagnostics: 2 | ClangTidy: 3 | Add: 4 | [ 5 | clang-analyzer-*, 6 | modernize-*, 7 | bugprone-*, 8 | performance-*, 9 | portability-*, 10 | readability-*, 11 | cppcoreguidelines-no-malloc, 12 | cppcoreguidelines-macro-usage, 13 | cppcoreguidelines-pro-bounds-pointer-arithmetic, 14 | cert-dcl21-cpp, 15 | cert-dcl58-cpp, 16 | cert-err34-c, 17 | cert-err52-cpp, 18 | cert-err58-cpp, 19 | cert-err60-cpp, 20 | cert-flp30-c, 21 | cert-msc50-cpp, 22 | cert-msc51-cpp, 23 | cert-str34-c, 24 | cppcoreguidelines-interfaces-global-init, 25 | cppcoreguidelines-narrowing-conversions, 26 | cppcoreguidelines-pro-type-member-init, 27 | cppcoreguidelines-pro-type-static-cast-downcast, 28 | cppcoreguidelines-slicing, 29 | google-default-arguments, 30 | google-explicit-constructor, 31 | google-runtime-operator, 32 | hicpp-exception-baseclass, 33 | hicpp-multiway-paths-covered, 34 | misc-misplaced-const, 35 | misc-new-delete-overloads, 36 | misc-no-recursion, 37 | misc-non-copyable-objects, 38 | misc-throw-by-value-catch-by-reference, 39 | misc-unconventional-assign-operator, 40 | misc-uniqueptr-reset-release, 41 | modernize-avoid-bind, 42 | modernize-concat-nested-namespaces, 43 | modernize-deprecated-headers, 44 | modernize-deprecated-ios-base-aliases, 45 | modernize-loop-convert, 46 | modernize-make-shared, 47 | modernize-make-unique, 48 | modernize-pass-by-value, 49 | modernize-raw-string-literal, 50 | modernize-redundant-void-arg, 51 | modernize-replace-auto-ptr, 52 | modernize-replace-disallow-copy-and-assign-macro, 53 | modernize-replace-random-shuffle, 54 | modernize-return-braced-init-list, 55 | modernize-shrink-to-fit, 56 | modernize-unary-static-assert, 57 | modernize-use-auto, 58 | modernize-use-bool-literals, 59 | modernize-use-emplace, 60 | modernize-use-equals-default, 61 | modernize-use-equals-delete, 62 | modernize-use-nodiscard, 63 | modernize-use-noexcept, 64 | modernize-use-nullptr, 65 | modernize-use-override, 66 | modernize-use-transparent-functors, 67 | modernize-use-uncaught-exceptions, 68 | openmp-use-default-none, 69 | performance-faster-string-find, 70 | ] 71 | Remove: 72 | [ 73 | readability-identifier-length, 74 | readability-magic-numbers, 75 | bugprone-easily-swappable-parameters, 76 | readability-function-cognitive-complexity, 77 | readability-suspicious-call-argument, 78 | readability-redundant-control-flow, 79 | readability-isolate-declaration, 80 | ] 81 | CompileFlags: 82 | Add: 83 | [ 84 | -W, 85 | -Wall, 86 | -Wshadow, 87 | -Wtype-limits, 88 | -Wasm, 89 | -Wchkp, 90 | -Warray-parameter, 91 | -Wthread-safety, 92 | -Wswitch-default, 93 | -Wuninitialized, 94 | -Wunused-label, 95 | -Wunused-lambda-capture, 96 | -Wno-error=unused-command-line-argument-hard-error-in-future, 97 | -Wno-sign-compare, 98 | -Wno-void-pointer-to-int-cast, 99 | -Wno-int-to-pointer-cast, 100 | -Wno-asm_invalid_global_var_reg, 101 | -Wno-format, 102 | -flto, 103 | -fsanitize=thread, 104 | -fsanitize=undefined, 105 | -fsanitize=dataflow, 106 | -fno-omit-frame-pointer, 107 | --target=aarch64-linux-gnu, 108 | ] 109 | Remove: 110 | [ 111 | -mabi=lp64, 112 | -fno-var-tracking-assignments, 113 | -fconserve-stack, 114 | -mpc-relative-literal-loads, 115 | ] 116 | 117 | 118 | InlayHints: 119 | Enabled: Yes 120 | ParameterNames: Yes 121 | DeducedTypes: Yes 122 | 123 | Hover: 124 | ShowAKA: Yes -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 已实现功能 2 | - [x] proc通过IO物理地址查询所有ioremap的虚拟地址 3 | - [x] proc通过符号查询符号里的内容,常用于指针变量 4 | - [x] proc通过符号查询符号地址 5 | - [x] proc列出所有已经插入的断点 6 | - [x] 通过导出的函数使用符号或地址添加删除断点 7 | - [x] proc通过符号和地址添加删除断点 8 | - [x] 支持断点触发次数统计 9 | 10 | 11 | ## 使用方法 12 | 13 | 这里只介绍proc接口的使用方法,代码里使用直接调对应的函数即可,接口均已导出。具体实现见[原理解析](./doc/硬件断点驱动解析.md)。 14 | 15 | ### 添加断点 16 | 17 | ``` 18 | echo add / > /proc/breakpoint, add a breakpoint 19 | [type]: 20 | [wp1]: HW_BREAKPOINT_R 21 | [wp2]: HW_BREAKPOINT_W 22 | [wp3]: HW_BREAKPOINT_R|HW_BREAKPOINT_W 23 | [bp]: HW_BREAKPOINT_X 24 | [len]:[0,8] (2^3,2^31] 25 | ``` 26 | 27 | 使用add指令可添加一个断点,长度可以是1~2^31的任意值,驱动会自动解析入参,设置一个大于期望监控地址范围的断点。触发时会根据期望监控的地址范围,选择是否打印堆栈。重复插入相同地址的断点会失败 28 | 29 | ### 删除断点 30 | 31 | `echo del / > /proc/breakpoint, del a breakpoint ` 32 | 33 | 删除一个断点。 34 | 35 | ### 查询符号地址或数据 36 | 37 | `echo get ptr/val > /proc/breakpoint, search &symbol/*(&symbol)` 38 | 39 | 有些时候想监控的地址是一个指针变量的地址的话,直接输入该符号名字,就无法监控了。所以提供了一个可以查询符号里内容的操作,可以查询指针变量里的指针。然后使用`echo add > /proc/breakpoint`打断点。也可以查询函数地址,然后在函数地址+N的地方设置执行断点。 40 | 41 | ### 列出所有已设置的断点 42 | 43 | `echo show > /proc/breakpoint` 44 | 45 | 该命令用于查询所有已设置的断点,信息示例如下所示: 46 | 47 | ``` 48 | -------------------------------------------------- 49 | breakpoint[6]: /*观察断点是6~9,执行断点是0~5*/ 50 | type: HW_BREAKPOINT_RW /*断点类型*/ 51 | name: zwf_test_value0+0x0/0xffffffffffffda78 [hw_break] /*断点的符号名称*/ 52 | monit: 0xffffff8000843588--->0xffffff800084358b /*断点期望监控的地址范围*/ 53 | len: 4 /*断点期望监控的长度*/ 54 | mask: 0x0 /*断点的mask掩码*/ 55 | range: 0xffffff8000843588--->0xffffff800084358c /*断点实际监控的地址范围*/ 56 | size: 4 /*断点实际监控的大小*/ 57 | -------------------------------------------------- 58 | breakpoint[7]: 59 | type: HW_BREAKPOINT_RW 60 | name: zwf_test_value1+0x0/0xffffffffffffd9f8 [hw_break] 61 | monit: 0xffffff8000843608--->0xffffff8000843610 62 | len: 9 63 | mask: 0x5 64 | range: 0xffffff8000843600--->0xffffff800084361f 65 | size: 31 66 | -------------------------------------------------- 67 | breakpoint[8]: 68 | type: HW_BREAKPOINT_RW 69 | name: zwf_test_value2+0x0/0xffffffffffffd978 [hw_break] 70 | monit: 0xffffff8000843688--->0xffffff80008436d4 71 | len: 77 72 | mask: 0x7 73 | range: 0xffffff8000843680--->0xffffff80008436ff 74 | size: 127 75 | -------------------------------------------------- 76 | breakpoint[9]: 77 | type: HW_BREAKPOINT_RW 78 | name: zwf_test_value3+0x0/0xffffffffffffdb00 [hw_break] 79 | monit: 0xffffff8000843500--->0xffffff800084357f 80 | len: 128 81 | mask: 0x8 82 | range: 0xffffff8000843500--->0xffffff80008435ff 83 | size: 255 84 | 85 | ``` 86 | 87 | ### 根据IO地址查询所有映射的虚拟地址 88 | 89 | `echo iophy > /proc/breakpoint` 90 | 该功能用于监测IO地址的更改,因为同一个IO地址可能被多个地方ioremap过,所以要监测IO地址的话就先找到该IO地址对应的所有虚拟地址。 91 | 示例: 92 | ``` 93 | /home # echo iophy 0x11030000 > /proc/breakpoint 94 | -------------------------------------------------- 95 | VM STRUCT: 96 | phy addr: 0x11030000 /*该vm_struct映射的物理起始地址*/ 97 | virt addr: 0xffffff800a135000 /*物理地址对应的虚拟地址起始地址*/ 98 | size: 0x2000 /*vm_struct映射的内存大小*/ 99 | 0x11030000 to virt: 0xffffff800a135000 /*要查询的IO地址对应的虚拟地址*/ 100 | 101 | -------------------------------------------------- 102 | VM STRUCT: 103 | phy addr: 0x11020000 104 | virt addr: 0xffffff800c780000 105 | size: 0x11000 106 | 0x11030000 to virt: 0xffffff800c790000 107 | 108 | -------------------------------------------------- 109 | VM STRUCT: 110 | phy addr: 0x10280000 111 | virt addr: 0xffffff8010000000 112 | size: 0x79d1000 113 | 0x11030000 to virt: 0xffffff8010db0000 114 | 115 | ``` 116 | 117 | -------------------------------------------------------------------------------- /hw_breakpoint_smp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ext_hw_breakpoint.h" 6 | 7 | typedef int (*hw_remote_func_f)(void *); 8 | 9 | /*func extern*/ 10 | extern int hw_bp_arch_parse(struct hw_bp_info *bp, const hw_bp_attr *attr, 11 | hw_bp_vc *hw); 12 | extern int hw_bp_install(struct hw_bp_info *bp); 13 | extern int hw_bp_uninstall(struct hw_bp_info *bp); 14 | extern int hw_arch_check_bp_in_kspace(hw_bp_vc *hw); 15 | 16 | struct hw_remote_func_call { 17 | struct hw_bp_info *p; 18 | hw_remote_func_f func; 19 | void *info; 20 | int ret; 21 | }; 22 | 23 | static void hw_remote_func(void *data) 24 | { 25 | struct hw_remote_func_call *tfc = data; 26 | 27 | /*callback*/ 28 | tfc->ret = tfc->func(tfc->info); 29 | } 30 | 31 | static int hw_cpu_func_call(int cpu, hw_remote_func_f func, void *info) 32 | { 33 | struct hw_remote_func_call data = { 34 | .p = NULL, 35 | .func = func, 36 | .info = info, 37 | .ret = -ENXIO, /* No such CPU */ 38 | }; 39 | 40 | preempt_disable(); 41 | if (cpu != smp_processor_id()) { 42 | smp_call_function_single(cpu, hw_remote_func, &data, 1); 43 | goto out; 44 | } 45 | 46 | data.ret = func(info); 47 | 48 | out: 49 | preempt_enable(); 50 | return data.ret; 51 | } 52 | 53 | static int hw_bp_parse(struct hw_bp_info *bp, const hw_bp_attr *attr, 54 | hw_bp_vc *hw) 55 | { 56 | int err; 57 | 58 | err = hw_bp_arch_parse(bp, attr, hw); 59 | if (err) 60 | return err; 61 | 62 | if (hw_arch_check_bp_in_kspace(hw)) { 63 | /*Don't let unprivileged users set a breakpoint in the trappath to avoid trap recursion attacks.*/ 64 | if (!capable(CAP_SYS_ADMIN)) 65 | return -EPERM; 66 | } 67 | 68 | return 0; 69 | } 70 | 71 | static int hw_bp_info_del(void *p) 72 | { 73 | struct hw_bp_info *bp = (struct hw_bp_info *)p; 74 | return hw_bp_uninstall(bp); 75 | } 76 | 77 | static int hw_bp_info_add(void *p) 78 | { 79 | struct hw_bp_info *bp = (struct hw_bp_info *)p; 80 | return hw_bp_install(bp); 81 | } 82 | 83 | static int hw_bp_info_init(struct hw_bp_info *bp) 84 | { 85 | int err; 86 | hw_bp_vc hw = {}; 87 | 88 | /*parse*/ 89 | err = hw_bp_parse(bp, &bp->attr, &hw); 90 | if (err) 91 | return err; 92 | 93 | bp->info = hw; 94 | 95 | return 0; 96 | } 97 | 98 | static struct hw_bp_info *hw_bp_info_alloc(const hw_bp_attr *attr, int cpu) 99 | { 100 | struct hw_bp_info *bp = NULL; 101 | int err; 102 | 103 | bp = kzalloc(sizeof(*bp), GFP_KERNEL); 104 | if (!bp) { 105 | pr_info("bp alloc fail\n"); 106 | return ERR_PTR(-ENOMEM); 107 | } 108 | 109 | bp->cpu = cpu; 110 | bp->attr = *attr; 111 | 112 | /*bp info init*/ 113 | err = hw_bp_info_init(bp); 114 | if (err) { 115 | pr_info("hw_bp_info_init fail\n"); 116 | return ERR_PTR(err); 117 | } 118 | /*smp_call_function_single in kgdb is error?*/ 119 | err = hw_cpu_func_call(cpu, hw_bp_info_add, bp); 120 | if (err) { 121 | pr_info("hw_bp_info_add fail\n"); 122 | return ERR_PTR(err); 123 | } 124 | 125 | return bp; 126 | } 127 | 128 | static void hw_bp_info_free(struct hw_bp_info *bp, int cpu) 129 | { 130 | hw_cpu_func_call(cpu, hw_bp_info_del, bp); 131 | kfree(bp); 132 | } 133 | 134 | void hw_bp_unregister(struct hw_bp_info *__percpu *bp, int state) 135 | { 136 | int cpu; 137 | 138 | if (bp == NULL) { 139 | return; 140 | } 141 | 142 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 143 | cpus_read_lock(); 144 | #else 145 | get_online_cpus(); 146 | #endif 147 | for_each_possible_cpu(cpu) { 148 | if (state & 1 << cpu) { 149 | hw_bp_info_free(per_cpu(*bp, cpu), cpu); 150 | } 151 | } 152 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 153 | cpus_read_unlock(); 154 | #else 155 | put_online_cpus(); 156 | #endif 157 | } 158 | 159 | int hw_bp_register(struct hw_bp_info *__percpu *cpu_events, hw_bp_attr *attr, 160 | int *state) 161 | { 162 | struct hw_bp_info *bp; 163 | int cpu; 164 | 165 | if (cpu_events == NULL || attr == NULL || state == NULL) { 166 | pr_info("hw_bp_register para is NULL\n"); 167 | return -1; 168 | } 169 | 170 | *state = 0; 171 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 172 | cpus_read_lock(); 173 | #else 174 | get_online_cpus(); 175 | #endif 176 | for_each_online_cpu(cpu) { 177 | bp = hw_bp_info_alloc(attr, cpu); 178 | if (IS_ERR(bp)) { 179 | pr_info("hw_bp_info_alloc error at CPU[%d]\n", cpu); 180 | } 181 | /*cpu success mask*/ 182 | *state |= 1 << cpu; 183 | /*percpu bp*/ 184 | per_cpu(*cpu_events, cpu) = bp; 185 | } 186 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 187 | cpus_read_unlock(); 188 | #else 189 | put_online_cpus(); 190 | #endif 191 | 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /ext_hw_breakpoint.h: -------------------------------------------------------------------------------- 1 | #ifndef __EXT_HW_BREAKPOINT_H 2 | #define __EXT_HW_BREAKPOINT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define HW_SYMS_FUNC(x) g_kernel_api.fun.x 9 | #define HW_SYMS_VAL(x) g_kernel_api.val.x 10 | 11 | typedef struct hw_trigger_times { 12 | u64 read; 13 | u64 write; 14 | u64 exec; 15 | } hw_trigger_times; 16 | 17 | typedef struct hw_bp_callback_data { 18 | u32 type; 19 | u64 addr; 20 | hw_trigger_times times; 21 | } hw_bp_callback_data; 22 | 23 | typedef void (*hw_bp_callback)(const hw_bp_callback_data *attr, 24 | const struct pt_regs *regs); 25 | 26 | typedef struct hw_bp_attr { 27 | u32 type; /*bp type*/ 28 | u64 addr; /*The addr of the bp expected to be monitored*/ 29 | u64 start_addr; /*The starting address of the actual monitoring*/ 30 | u64 end_addr; /*The end address of the actual monitoring*/ 31 | u64 len; /*The length of the bp expected to be monitored*/ 32 | u64 real_len; /*LBN len*/ 33 | u32 mask; /*addr mask*/ 34 | hw_trigger_times times; /*trigger times*/ 35 | hw_bp_callback handler; /*user handler*/ 36 | u64 disabled : 1, //63bit 37 | reserved : 63; //0~62bit 38 | } hw_bp_attr; 39 | 40 | /*struct of get info*/ 41 | typedef struct hw_bp_report { 42 | u32 type; /*bp type*/ 43 | u64 addr; /*The addr of the bp expected to be monitored*/ 44 | u64 len; /*The length of the bp expected to be monitored*/ 45 | u32 mask; 46 | hw_trigger_times times; /*trigger times*/ 47 | } hw_bp_report; 48 | typedef struct hw_bp_info_list { 49 | struct list_head list; /*list*/ 50 | hw_bp_report *attr; /*bp attr. attr[cpu_id]*/ 51 | int cpu_mask; /*success install of cpu*/ 52 | int cpu_num; /*total cpu num*/ 53 | } hw_bp_info_list; 54 | 55 | typedef struct hw_bp_ctrl_reg { 56 | u32 reserved2 : 3, //29~31bit, 57 | mask : 5, //24~28bit, addr mask,mask=0b11111: (mask2^0b11111 the low bit addr), support 8~2G range 58 | reserved1 : 3, //21~23bit, 59 | wt : 1, //20bit, watchpoint type, Unlinked(0)/linked(1) data address match. 60 | lbn : 4, //16~19bit, WT is only required to be set when setting, which is related to link breakpoints 61 | ssc : 2, //14,15bit, Security state control, which controls what state will listen for breakpoint events 62 | hmc : 1, //13bit, Use in conjunction with the above fields 63 | len : 8, //5~12bit, LBN of len, Each bit represents 1 byte and a maximum of 8 bytes 64 | type : 2, //3~4bit, bp type wp/bp 65 | privilege : 2, //1~2bit, The EL level at the time of the last breakpoint setting is used with SSC and HMC 66 | enabled : 1; //0bit, bp enable 67 | } hw_bp_ctrl_reg; 68 | 69 | typedef struct hw_bp_vc { 70 | u64 address; 71 | hw_bp_ctrl_reg ctrl; 72 | u64 trigger; 73 | u8 access_type; 74 | } hw_bp_vc; 75 | 76 | struct hw_bp_info { 77 | int cpu; 78 | hw_bp_attr attr; 79 | hw_bp_vc info; 80 | }; 81 | 82 | struct fault_info { 83 | int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs); 84 | int sig; 85 | int code; 86 | const char *name; 87 | }; 88 | typedef struct hw_kernel_api { 89 | struct { 90 | unsigned long (*kallsyms_lookup_name)( 91 | const char *name); /*search symbols func*/ 92 | void (*register_step_hook)(struct step_hook *hook); 93 | void (*unregister_step_hook)(struct step_hook *hook); 94 | void (*enable_debug_monitors)(enum dbg_active_el el); 95 | void (*disable_debug_monitors)(enum dbg_active_el el); 96 | int (*kernel_active_single_step)(void); 97 | void (*kernel_enable_single_step)(struct pt_regs *regs); 98 | void (*kernel_disable_single_step)(void); 99 | u64 (*read_sanitised_ftr_reg)(u32 id); 100 | void (*show_regs)(struct pt_regs *); 101 | void (*dump_backtrace)(struct pt_regs *regs, 102 | struct task_struct *tsk); 103 | void (*do_bad)(unsigned long addr, unsigned int esr, 104 | struct pt_regs *regs); 105 | } __aligned(128) fun; 106 | struct { 107 | #ifdef CONFIG_CPU_PM 108 | u64 *hw_breakpoint_restore; 109 | u64 default_hw_breakpoint_restore; 110 | #endif 111 | struct fault_info *debug_fault_info; 112 | struct fault_info default_fault_info[2]; 113 | spinlock_t *vmap_area_lock; /*kernel vm spinlock*/ 114 | struct list_head *vmap_area_list; /*kernel vm list*/ 115 | } __aligned(128) val; 116 | 117 | } hw_kernel_api; 118 | 119 | extern hw_kernel_api g_kernel_api; 120 | 121 | /*encode reg*/ 122 | static inline u32 hw_encode_ctrl_reg(hw_bp_ctrl_reg ctrl) 123 | { 124 | u32 val = (ctrl.mask << 24) | (ctrl.len << 5) | (ctrl.type << 3) | 125 | (ctrl.privilege << 1) | ctrl.enabled; 126 | 127 | if (is_kernel_in_hyp_mode() && ctrl.privilege == AARCH64_BREAKPOINT_EL1) 128 | val |= DBG_HMC_HYP; 129 | 130 | return val; 131 | } 132 | 133 | /*decode reg*/ 134 | static inline void hw_decode_ctrl_reg(u32 reg, hw_bp_ctrl_reg *ctrl) 135 | { 136 | ctrl->enabled = reg & 0x1; 137 | reg >>= 1; 138 | ctrl->privilege = reg & 0x3; 139 | reg >>= 2; 140 | ctrl->type = reg & 0x3; 141 | reg >>= 2; 142 | ctrl->len = reg & 0xff; 143 | reg >>= 19; 144 | ctrl->mask = reg & 0x1f; 145 | } 146 | 147 | static inline hw_bp_vc *hw_get_vc(struct hw_bp_info *bp) 148 | { 149 | return &bp->info; 150 | } 151 | 152 | /* Determine number of BRP registers available. */ 153 | static inline int hw_get_num_brps(void) 154 | { 155 | u64 dfr0 = HW_SYMS_FUNC(read_sanitised_ftr_reg)(SYS_ID_AA64DFR0_EL1); 156 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 157 | return 1 + cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_BRPs_SHIFT); 158 | #else 159 | return 1 + cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_BRPS_SHIFT); 160 | #endif 161 | } 162 | 163 | /* Determine number of WRP registers available. */ 164 | static inline int hw_get_num_wrps(void) 165 | { 166 | u64 dfr0 = HW_SYMS_FUNC(read_sanitised_ftr_reg)(SYS_ID_AA64DFR0_EL1); 167 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 168 | return 1 + cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_EL1_WRPs_SHIFT); 169 | #else 170 | return 1 + cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_WRPS_SHIFT); 171 | #endif 172 | } 173 | 174 | int hw_get_bp_num(int type); 175 | void hw_proc_exit(void); 176 | void hw_bp_manage_deinit(void); 177 | 178 | /*user handler*/ 179 | /*install/uninstall*/ 180 | int hw_bp_install_from_addr(u64 addr, int len, int type, 181 | hw_bp_callback handler); 182 | void hw_bp_uninstall_from_addr(u64 addr); 183 | int hw_bp_install_from_symbol(char *name, int len, int type, 184 | hw_bp_callback handler); 185 | void hw_bp_uninstall_from_symbol(char *name); 186 | /*get install bp info*/ 187 | hw_bp_info_list *hw_get_bp_infos(void); 188 | void hw_free_bp_infos(hw_bp_info_list *info); 189 | 190 | #endif -------------------------------------------------------------------------------- /hw_breakpoint_proc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "linux/printk.h" 9 | #include 10 | #include "ext_hw_breakpoint.h" 11 | #include "hw_breakpoint_until.h" 12 | 13 | #define PROC_FILE_DEBUG "breakpoint" 14 | 15 | /*func extern*/ 16 | extern void hw_bp_show_all(void); 17 | 18 | /*proc_file handle*/ 19 | static struct proc_dir_entry *proc_file = NULL; 20 | 21 | /*help*/ 22 | char *hw_proc_write_usag = { 23 | "Usage:\n" 24 | "\thw_break support cmd type: \n" 25 | "\t\t1: echo add / > /proc/breakpoint, add a breakpoint\n" 26 | "\t\t\t[type]:\n" 27 | "\t\t\t\t[wp1]: HW_BREAKPOINT_R\n" 28 | "\t\t\t\t[wp2]: HW_BREAKPOINT_W\n" 29 | "\t\t\t\t[wp3]: HW_BREAKPOINT_R|HW_BREAKPOINT_W\n" 30 | "\t\t\t\t[bp]: HW_BREAKPOINT_X\n" 31 | "\t\t\t[len]:[0,8] (2^3,2^31]\n" 32 | "\t\t2: echo del > /proc/breakpoint, del a breakpoint\n" 33 | "\t\t3: echo get ptr/val > /proc/breakpoint, search &symbol/*(&symbol)\n" 34 | "\t\t4: echo iophy > /proc/breakpoint, search all of ioaddr map virt\n" 35 | }; 36 | /*example*/ 37 | char *hw_proc_write_example = { 38 | "Example:\n" 39 | "\tThe first step:\n" 40 | "\t\techo add wp3 4 hw_test_value0 > /proc/breakpoint, add a watchpoint at " 41 | "&hw_test_value0\n" 42 | "\tThe second step:\n" 43 | "\t\techo write 0 0 > /proc/breakpoint, write hw_test_value0\n" 44 | "\tThe third step:\n" 45 | "\t\techo read 0 0 > /proc/breakpoint, read hw_test_value0\n" 46 | "\tThe forth step:\n" 47 | "\t\techo del hw_test_value0 > /proc/breakpoint, del wawtchpoint at " 48 | "&hw_test_value0\n" 49 | }; 50 | 51 | /*seq show*/ 52 | static int hw_proc_show(struct seq_file *m, void *v) 53 | { 54 | hw_bp_info_list *info = NULL, *node = NULL; 55 | int i = 0, index = 0; 56 | 57 | /*get info*/ 58 | info = hw_get_bp_infos(); 59 | if (info) { 60 | list_for_each_entry(node, &info->list, list) { 61 | for (i = 0; i < node->cpu_num; i++) { 62 | if (node->cpu_mask & (1 << i)) { 63 | break; 64 | } 65 | } 66 | seq_printf(m, "----------------[%d]----------------\n", 67 | index++); 68 | seq_printf(m, "type: \t0x%x\n", node->attr[i].type); 69 | seq_printf(m, "addr: \t0x%llx\n", node->attr[i].addr); 70 | seq_printf(m, "len: \t0x%llx\n", node->attr[i].len); 71 | seq_printf(m, "mask: \t0x%x\n", node->attr[i].mask); 72 | for (i = 0; i < node->cpu_num; i++) { 73 | if (!(node->cpu_mask & (1 << i))) { 74 | continue; 75 | } 76 | seq_printf(m, "cpu[%d] trigger times:\n", i); 77 | seq_printf( 78 | m, 79 | "\tread: %llu, write: %llu, exec: %llu\n", 80 | node->attr[i].times.read, 81 | node->attr[i].times.write, 82 | node->attr[i].times.exec); 83 | } 84 | } 85 | hw_free_bp_infos(info); 86 | } 87 | return 0; 88 | } 89 | 90 | static int hw_proc_open(struct inode *inode, struct file *file) 91 | { 92 | return single_open(file, hw_proc_show, inode->i_private); 93 | } 94 | 95 | u32 hw_test_value3[32] = { 0 }; 96 | u32 hw_test_value2[32] = { 0 }; 97 | u32 hw_test_value1[32] = { 0 }; 98 | u32 hw_test_value0[32] = { 0 }; 99 | 100 | /*show vm info*/ 101 | static void hw_show_vm(struct vm_struct *area, u64 phy_addr) 102 | { 103 | pr_info("--------------------------------------------------\n"); 104 | if (area->phys_addr) { 105 | pr_info("\tphy addr:\t0x%llx\n", area->phys_addr); 106 | } 107 | if (area->addr) { 108 | pr_info("\tvirt addr:\t0x%llx\n", (u64)area->addr); 109 | } 110 | if (area->size) { 111 | pr_info("\tsize:\t\t0x%lx\n", area->size); 112 | } 113 | if (area->addr && area->phys_addr) { 114 | pr_info("0x%llx to virt: 0x%llx\n", phy_addr, 115 | (u64)area->addr + phy_addr - area->phys_addr); 116 | } 117 | pr_info("\n"); 118 | } 119 | 120 | /*proc get virt of iophys*/ 121 | static void hw_iophy_to_virt(char *addr_buf) 122 | { 123 | u64 io_addr = 0; 124 | iophys_info *iophys = NULL, *node = NULL; 125 | 126 | io_addr = simple_strtol(addr_buf, NULL, 0); 127 | iophys = get_iophys_info(io_addr); 128 | 129 | if (iophys) { 130 | list_for_each_entry(node, &iophys->list, list) { 131 | hw_show_vm(&node->area, io_addr); 132 | } 133 | } 134 | free_iophys_info(iophys); 135 | } 136 | 137 | /*proc get handler*/ 138 | static int hw_proc_get(char *type_buf, char *name_buf) 139 | { 140 | u64 addr = 0; 141 | 142 | /*get symbol addr*/ 143 | addr = HW_SYMS_FUNC(kallsyms_lookup_name)(name_buf); 144 | if (!addr || addr < TASK_SIZE) { 145 | pr_info("can not find symbol %s\n", name_buf); 146 | return -1; 147 | } 148 | if (strcmp("ptr", type_buf) == 0) { 149 | pr_info("&%s = 0x%llx\n", name_buf, addr); 150 | } else if (strcmp("val", type_buf) == 0) { 151 | pr_info("*(%s) = 0x%llx\n", name_buf, *((u64 *)addr)); 152 | } else { 153 | return -1; 154 | } 155 | return 0; 156 | } 157 | 158 | /*proc del bp*/ 159 | static void hw_proc_del(char *name_buf) 160 | { 161 | u64 uninstall_addr = 0; 162 | 163 | if (name_buf[0] == '0' && name_buf[1] == 'x') { 164 | uninstall_addr = simple_strtol(name_buf, 0, 0); 165 | } 166 | if (uninstall_addr) { 167 | pr_info("will uninstall at 0x%llx\n", uninstall_addr); 168 | hw_bp_uninstall_from_addr(uninstall_addr); 169 | } else { 170 | pr_info("will uninstall at &%s\n", name_buf); 171 | hw_bp_uninstall_from_symbol(name_buf); 172 | } 173 | } 174 | 175 | /*proc add bp*/ 176 | static int hw_proc_add(char *type_buf, char *len_buf, char *name_buf) 177 | { 178 | char *name = NULL; 179 | int len = HW_BREAKPOINT_LEN_4, type = 0; 180 | u64 install_addr = 0; 181 | 182 | /*check bp type*/ 183 | switch (strlen(type_buf)) { 184 | /*The length is 2 for the bp*/ 185 | case 2: { 186 | type = HW_BREAKPOINT_X; 187 | name = name_buf; 188 | break; 189 | } 190 | /*The length is 3 for the wp, and the third character is the breakpoint type*/ 191 | case 3: { 192 | type = type_buf[2] - '0'; 193 | len = (int)simple_strtoul(len_buf, NULL, 0); 194 | name = name_buf; 195 | break; 196 | } 197 | default: { 198 | return -1; 199 | } 200 | } 201 | /*check type if valid*/ 202 | if (type < 1 || type > 4) { 203 | return -1; 204 | } 205 | 206 | if (name_buf[0] == '0' && name_buf[1] == 'x') { 207 | install_addr = simple_strtol(name_buf, 0, 0); 208 | } 209 | if (install_addr) { 210 | pr_info("will install at 0x%llx\n", install_addr); 211 | hw_bp_install_from_addr(install_addr, len, type, NULL); 212 | } else { 213 | pr_info("will install at &%s\n", name); 214 | hw_bp_install_from_symbol(name, len, type, NULL); 215 | } 216 | return 0; 217 | } 218 | 219 | /*test write*/ 220 | static void hw_proc_rw_test(char *cmd, char *index_of_buf, char *index_in_buf) 221 | { 222 | int index = (int)simple_strtol(index_of_buf, NULL, 0); 223 | int index1 = (int)simple_strtol(index_in_buf, NULL, 0); 224 | u32 *tmpbuf; 225 | switch (index) { 226 | case 0: { 227 | tmpbuf = hw_test_value0; 228 | break; 229 | } 230 | case 1: { 231 | tmpbuf = hw_test_value1; 232 | break; 233 | } 234 | case 2: { 235 | tmpbuf = hw_test_value2; 236 | break; 237 | } 238 | case 3: 239 | default: { 240 | tmpbuf = hw_test_value3; 241 | break; 242 | } 243 | } 244 | if (strcmp("write", cmd) == 0) { 245 | pr_info("will write hw_test_value%d[%d], addr = %llx\n", index, 246 | index1, (u64)&tmpbuf[index1]); 247 | tmpbuf[index1] = get_random_u32(); 248 | } else if (strcmp("read", cmd) == 0) { 249 | pr_info("will read hw_test_value%d[%d], addr = %llx\n", index, 250 | index1, (u64)&tmpbuf[index1]); 251 | pr_info("hw_test_value%d[%d] = %d\n", index, index1, 252 | tmpbuf[index1]); 253 | } 254 | } 255 | 256 | static ssize_t hw_proc_write(struct file *file, const char __user *p_buf, 257 | size_t count, loff_t *pPos) 258 | { 259 | size_t ret; 260 | char cmd_buf[128] = { 0 }; 261 | int argc = 0; 262 | char *argv[10] = { NULL }; 263 | 264 | // pr_info("hw_proc_write\n"); 265 | 266 | if ((count > sizeof(cmd_buf)) || (count == 0)) { 267 | pr_info("test proc write, count is error!\n"); 268 | return (ssize_t)count; 269 | } 270 | 271 | memset(cmd_buf, 0, sizeof(cmd_buf)); 272 | ret = copy_from_user(cmd_buf, p_buf, count); 273 | if (0 != ret) { 274 | pr_info("fail to copy data from user!\n"); 275 | return (ssize_t)count; 276 | } 277 | 278 | cmd_buf[count - 1] = '\0'; 279 | memset(argv, 0, sizeof(argv)); 280 | process_cmd_string(cmd_buf, &argc, argv); 281 | 282 | if (strcmp("write", argv[0]) == 0 || strcmp("read", argv[0]) == 0) { 283 | if (argc != 3) { 284 | goto cmdErr; 285 | } 286 | hw_proc_rw_test(argv[0], argv[1], argv[2]); 287 | return (ssize_t)count; 288 | } else if (strcmp("show", argv[0]) == 0) { 289 | hw_bp_show_all(); 290 | return (ssize_t)count; 291 | } else if (strcmp("help", argv[0]) == 0) { 292 | pr_info("%s", hw_proc_write_usag); 293 | pr_info("%s", hw_proc_write_example); 294 | return (ssize_t)count; 295 | } 296 | 297 | if (strcmp("add", argv[0]) == 0) { 298 | if (argc != 4) { 299 | // pr_info("argc = %d\n",argc); 300 | goto cmdErr; 301 | } 302 | if (hw_proc_add(argv[1], argv[2], argv[3])) { 303 | goto cmdErr; 304 | } 305 | } else if (strcmp("del", argv[0]) == 0) { 306 | if (argc != 2) { 307 | // pr_info("argc = %d\n",argc); 308 | goto cmdErr; 309 | } 310 | hw_proc_del(argv[1]); 311 | } else if (strcmp("get", argv[0]) == 0) { 312 | if (argc != 3) { 313 | // pr_info("argc = %d\n",argc); 314 | goto cmdErr; 315 | } 316 | if (hw_proc_get(argv[1], argv[2])) { 317 | goto cmdErr; 318 | } 319 | } else if (strcmp("iophy", argv[0]) == 0) { 320 | if (argc != 2) { 321 | // pr_info("argc = %d\n",argc); 322 | goto cmdErr; 323 | } 324 | hw_iophy_to_virt(argv[1]); 325 | } else { 326 | goto cmdErr; 327 | } 328 | 329 | return (ssize_t)count; 330 | cmdErr: 331 | pr_info("cmd error, echo help > /proc/breakpoint\n"); 332 | return (ssize_t)count; 333 | } 334 | 335 | #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 1, 10) 336 | static const struct proc_ops hw_proc_fops = { 337 | .proc_open = hw_proc_open, 338 | .proc_write = hw_proc_write, 339 | .proc_read = seq_read, 340 | .proc_lseek = seq_lseek, 341 | .proc_release = single_release, 342 | }; 343 | #else 344 | static const struct file_operations hw_proc_fops = { 345 | .proc_open = hw_proc_open, 346 | .proc_write = hw_proc_write, 347 | .proc_read = seq_read, 348 | .proc_lseek = seq_lseek, 349 | .proc_release = single_release, 350 | }; 351 | #endif 352 | 353 | int hw_proc_init(void) 354 | { 355 | proc_file = proc_create(PROC_FILE_DEBUG, S_IRUGO | S_IWUGO, NULL, 356 | &hw_proc_fops); 357 | if (NULL == proc_file) { 358 | pr_info("hw proc init, Create %s proc file failed!\n", 359 | PROC_FILE_DEBUG); 360 | return -ENOMEM; 361 | } 362 | // pr_info(hw_proc_write_usag); 363 | // pr_info(hw_proc_write_example); 364 | return 0; 365 | } 366 | 367 | void hw_proc_exit(void) 368 | { 369 | if (NULL != proc_file) { 370 | remove_proc_entry(PROC_FILE_DEBUG, NULL); 371 | } 372 | } 373 | -------------------------------------------------------------------------------- /doc/硬件断点驱动解析.md: -------------------------------------------------------------------------------- 1 | 2 | # 硬件断点是什么 3 | 4 | 硬件断点是cpu自带的用于调试程序的断点,分为执行断点和内存断点。比如armv8架构,它有4个内存断点和6个执行断点。执行断点是最常见的断点类型,就是在某个程序地址处加断点,CPU运行到这里时就会触发,触发长度是4字节。而内存断点是,监控某一个内存区间,当CPU试图访问或者是操作这块内存时就会触发,触发长度可以是[1,8]字节,或是(8,2G]字节,其中8~2G的区间内只能是2的幂。 5 | 6 | # 硬件断点的实现机制 7 | 8 | cpu的架构不同,硬件断点的机制也不同。通常都是配置好对应的断点寄存器后,CPU触发断点,触发调试异常,进入中断执行相应操作后退出。大概流程如下图所示: 9 | 10 | ```mermaid 11 | flowchart TB 12 | CONFIG_BKREG([配置断点寄存器]) 13 | CPU_RUN([CPU正常运行]) 14 | CPU_TRIGER([CPU访问断点内的地址]) 15 | INTO_ESR([CPU发生异常]) 16 | DECODE_ESRISS([解码ISS得到异常类型为ESR_ELx_EC_BREAKPT_CUR]) 17 | GET_ESR_ENTRY([从异常向量表中找到ESR_ELx_EC_BREAKPT_CUR对应的服务函数]) 18 | DEBUG_HANDLE([执行服务函数]) 19 | 20 | CONFIG_BKREG-->CPU_RUN 21 | CPU_RUN-->CPU_TRIGER 22 | CPU_TRIGER-->INTO_ESR 23 | INTO_ESR-->DECODE_ESRISS 24 | DECODE_ESRISS-->GET_ESR_ENTRY 25 | GET_ESR_ENTRY-->DEBUG_HANDLE 26 | 27 | DEBUG_HANDLE-->CPU_RUN 28 | ``` 29 | 30 | ## 断点寄存器解析 31 | 32 | 以armv8为例,比如cotex-a55核心,每个断点都有2个用于控制硬件断点的寄存器。如下所示:()下面表格中的N代表第几个断点,X代表异常等级,比如el0, el1, el2) 33 | 34 | | name | explain | 35 | | ------------- | ---------------------- | 36 | | `dbgbvrN_elX` | 执行断点地址设置寄存器 | 37 | | `dbgbcrN_elX` | 执行断点控制寄存器 | 38 | | `dbgwvrN_elX` | 观察断点地址设置寄存器 | 39 | | `dbgwcrN_elX` | 观察断点控制寄存器 | 40 | 41 | ### `dbgbvrN_elX` 42 | 43 | 设置执行断点的地址,linux里直接填入对应函数的虚拟地址即可。 44 | 45 | ### `dbgbcrN_elX` 46 | 47 | 执行断点的控制寄存器,共有31BIT。用于控制断点的各种属性。具体如下所示: 48 | 49 | | 31~24 | 23~20 | 19~16 | 15~14 | 13 | 12~9 | 8~5 | 4~3 | 2~1 | 0 | 50 | | :---: | :---: | :---: | :---: | :--: | :--: | :--: | :--: | :--: | :--: | 51 | | RES2 | BT | LBN | SSC | HMC | RES1 | BAS | RES0 | PMC | E | 52 | 53 | 其中比较常用的是E, PMC, BT。 54 | 55 | * E是断点使能位,高位使能。 56 | * PMC用于记录上次控制时的异常等级,配合HMC和SSC一起使用 57 | * BT是控制断点触发的条件,默认是0b0000,对应的触发条件是当地址匹配时触发。 58 | 59 | ### `dbgwvrN_elX` 60 | 61 | 设置观察断点的地址,linux里直接填入想观察的内存区域的起始地址即可。 62 | 63 | ### `dbgwcrN_elX` 64 | 65 | 观察断点的控制寄存器,共有31bit,用于控制观察断点的各种属性。具体如下所示: 66 | 67 | | 31~29 | 28~24 | 23~21 | 20 | 19~16 | 15~14 | 13 | 12~5 | 4~3 | 2~1 | 0 | 68 | | ----- | :---: | :---: | :--: | :---: | :---: | :--: | :--: | :--: | :--: | :--: | 69 | | RES1 | MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E | 70 | 71 | 其中比较常用的是E, PAC, BAS, MASK。 72 | 73 | * E是断点使能位,高位使能。 74 | * PAC用于记录上次控制时的异常等级,配合HMC和SSC一起使用 75 | * BAS一共8bit,代表要监控的内存大小,每1bit代表1byte,最大可检测8bytes。 76 | * MASK代表使用掩码的模式监控地址,共有5bit,代表可以掩掉dbgwvrN_elX中的0~31位低地址。 77 | 78 | #### MASK举例 79 | 80 | 比如想监控的地址是 `0xffffffc0622a5c10, MASK=0b00111` 81 | 82 | 则监控的范围是:`0xffffffc0622a5c00~0xffffffc0622a5c7f`,实际的 `dbgwcrN_elX=0xffffffc0622a5c00` 83 | 84 | 假如 `MASK=0b01100` 85 | 86 | 则监控的范围是:`0xffffffc0622a5000~0xffffffc0622a5fff`,实际的 `dbgwcrN_elX=0xffffffc0622a5000` 87 | 88 | # 实现硬件断点驱动 89 | 90 | 在linux内核里其实有硬件断点功能,但是这个硬件断点不太好用,它依赖太多的配置选项。开了这些配置选项除了内核需要重编之外,由于头文件变更过大,所以所有的驱动也要重编。而且内核的硬件断点只能监控8字节,也不支持设置断点触发事件,自定义属性太少。 91 | 92 | ## 硬件断点需要实现的部分 93 | 94 | 实现硬件断点驱动,主要是需要实现以下几部分: 95 | 96 | 1. 注册自己的断点异常服务函数。 97 | 2. 根据设定的地址和长度自动解析需配置的硬件断点寄存器值。 98 | 3. 设置硬件断点寄存器。 99 | 100 | ## 硬件断点实现需要的内核接口 101 | 102 | 上述部分的实现依赖一些内核接口,这些接口内核是没有导出符号的,不过只要开启了 `CONFIG_KALLSYMS=y`,函数都是可以查询的到的。 103 | 104 | 涉及的接口如下: 105 | 106 | ```c 107 | typedef struct HW_kernelApi 108 | { 109 | struct 110 | { 111 | unsigned long (*kallsyms_lookup_name)(const char *name); /*根据符号查询地址函数*/ 112 | void (*register_step_hook)(struct step_hook *hook); /*注册step调试异常hook的函数*/ 113 | void (*unregister_step_hook)(struct step_hook *hook); /*取消注册step调试异常hook的函数*/ 114 | void (*enable_debug_monitors)(enum dbg_active_el el); /*使能debug异常*/ 115 | void (*disable_debug_monitors)(enum dbg_active_el el); /*失能debug异常*/ 116 | int (*kernel_active_single_step)(void); /*单步调试是否激活*/ 117 | void (*kernel_enable_single_step)(struct pt_regs *regs); /*使能单步调试异常*/ 118 | void (*kernel_disable_single_step)(void); /*失能单步调试异常*/ 119 | u64 (*read_sanitised_ftr_reg)(u32 id); /*读ftr寄存器*/ 120 | void (*show_regs)(struct pt_regs *); /*显示堆栈*/ 121 | void (*do_bad)(unsigned long addr, unsigned int esr, struct pt_regs *regs); /*调试异常的默认中断处理函数*/ 122 | } __aligned(128) fun; 123 | struct 124 | { 125 | #ifdef CONFIG_CPU_PM 126 | u64 *hw_breakpoint_restore; /*cpu从调试暂停恢复运行时执行的函数*/ 127 | u64 default_hw_breakpoint_restore; /*接管之前的函数地址*/ 128 | #endif 129 | struct fault_info *debug_fault_info; /*接管硬件断点调试异常中断,替换回调函数*/ 130 | struct fault_info default_fault_info[2]; /*接管之前的数据信息*/ 131 | } __aligned(128) val; 132 | } HW_kernelApi; 133 | ``` 134 | 135 | 我记得在linux 5.7.0之后的内核版本,kallsyms_lookup_name这个根据符号查询地址的函数,是不再导出了。所以驱动中需要先找到这个符号的地址,再用这个地址去查询其余需要的内核接口即可。寻找kallsyms_lookup_name的代码如下: 136 | 137 | ```c 138 | /*根据名字找函数地址*/ 139 | unsigned long kaddr_lookup_name(const char *fname_raw) 140 | { 141 | int i; 142 | unsigned long kaddr; 143 | char *fname_lookup, *fname; 144 | 145 | fname_lookup = kzalloc(NAME_MAX, GFP_KERNEL); 146 | if (!fname_lookup) 147 | return 0; 148 | 149 | fname = kzalloc(strlen(fname_raw) + 4, GFP_KERNEL); 150 | if (!fname) 151 | return 0; 152 | 153 | /*第一个0x0代表是该符号的起始地址*/ 154 | strcpy(fname, fname_raw); 155 | strcat(fname, "+0x0"); 156 | 157 | /*获取内核代码段基地址*/ 158 | kaddr = (unsigned long)&sprint_symbol; 159 | kaddr &= 0xffffffffff000000; 160 | 161 | /*内核符号不会超过0x100000*16的大小,所以按4字节偏移,挨个找*/ 162 | for (i = 0x0; i < 0x400000; i++) 163 | { 164 | /*寻找地址对应的符号名称*/ 165 | sprint_symbol(fname_lookup, kaddr); 166 | /*对比寻找的符号名字*/ 167 | if (strncmp(fname_lookup, fname, strlen(fname)) == 0) 168 | { 169 | /*找到了就返回地址*/ 170 | kfree(fname_lookup); 171 | kfree(fname); 172 | return kaddr; 173 | } 174 | /*偏移4字节*/ 175 | kaddr += 0x04; 176 | } 177 | /*没找到地址就返回0*/ 178 | kfree(fname_lookup); 179 | kfree(fname); 180 | return 0; 181 | } 182 | ``` 183 | 184 | ## 解析要设置断点信息 185 | 186 | 对于使用者来说,是不需要关注每个断点寄存器应该配什么值,只需要知道以下几点就行了: 187 | 188 | 1. 断点类型:执行断点或是读/写断点。 189 | 2. 断点地址 190 | 3. 断点监控的长度 191 | 192 | 所以这部分需要解析对应调用者传入的参数,自动解析出寄存器需要配置的值。流程如下: 193 | 194 | ```mermaid 195 | flowchart LR 196 | %% 设置曲线样式 197 | %%{ init: { 'flowchart': { 'curve': 'basis' } } }%% 198 | TYPE([BK TYPE]) 199 | ADDR([BK ADDR]) 200 | LEN([BK LEN]) 201 | 202 | subgraph BP 203 | direction LR 204 | dbgbvr([dbgbvr=addr]) 205 | dbgbcr([dbgbcr.EN=1,\n.BT=0\n.bas=0b1111]) 206 | 207 | end 208 | TYPE-->BP 209 | ADDR-->dbgbvr 210 | LEN-->|len=4|dbgbcr 211 | 212 | subgraph WP 213 | direction TB 214 | len[[len>8?]] 215 | BASWl([dbgwcr.bas=0blen*1]) 216 | dbgwvr([dbgwvr=addr]) 217 | 218 | getBaseMask(["mask=log2(len)"]) 219 | ifMask[["2^mask*bit=1 > addr+len?"]] 220 | maskAdd(["mask=mask+1"]) 221 | MASK(["dbgwcr.mask=mask"]) 222 | 223 | 224 | 225 | 226 | len-->|否|BASWl 227 | len-->|是|getBaseMask 228 | getBaseMask-->ifMask 229 | ifMask-->|是|MASK 230 | ifMask-->|否|maskAdd 231 | maskAdd-->ifMask 232 | end 233 | 234 | LEN-->len 235 | ADDR-->dbgwvr 236 | TYPE-->WP 237 | 238 | ``` 239 | 240 | ## 实现断点异常服务函数 241 | 242 | 以观察断点服务函数为例,当一个观察断点触发时需要如下几步。 243 | 244 | 1. 先关闭所有观察断点,防止其一直触发。 245 | 2. 遍历所有已设置的观察断点,求出触发的地址与设置的地址正向距离最近的那个断点。 246 | 3. 取出断点数据结构,筛选该地址是否在期望监控的地址范围内。 247 | 4. 执行用户函数。 248 | 5. 在当前regs开启single step 249 | 6. 进入single step异常服务函数 250 | 7. 重新开启断点。 251 | 252 | 至此整个流程完毕,执行断点流程类似。代码如下所示: 253 | 254 | ```c 255 | /*watchpoint回调函数*/ 256 | static int HW_watchpointHandler(unsigned long addr, unsigned int esr, struct pt_regs *regs) 257 | { 258 | int i, *kernel_step, access, closest_match = -1; 259 | u64 min_dist = -1, dist; 260 | u32 ctrl_reg; 261 | u64 val, startAddr, endAddr; 262 | struct HW_breakpointInfo *wp, **slots; 263 | // struct debug_info *debug_info; 264 | HW_breakpointVC *info = NULL; 265 | HW_breakpointCtrlReg ctrl; 266 | 267 | slots = this_cpu_ptr(wp_on_reg); 268 | // debug_info = ¤t->thread.debug; 269 | 270 | /* 271 | * Find all watchpoints that match the reported address. If no exact 272 | * match is found. Attribute the hit to the closest watchpoint. 273 | */ 274 | rcu_read_lock(); 275 | for (i = 0; i < core_num_wrps; ++i) 276 | { 277 | wp = slots[i]; 278 | if (wp == NULL) 279 | continue; 280 | 281 | /* 282 | * Check that the access type matches. 283 | * 0 => load, otherwise => store 284 | */ 285 | access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : HW_BREAKPOINT_R; 286 | if (!(access /*& hw_breakpoint_type(wp)待实现,将wp与attr->type关联*/)) 287 | continue; 288 | 289 | /* Check if the watchpoint value and byte select match. */ 290 | val = HW_readBreakpointReg(AARCH64_DBG_REG_WVR, i); 291 | ctrl_reg = HW_readBreakpointReg(AARCH64_DBG_REG_WCR, i); 292 | HW_decodeCtrlReg(ctrl_reg, &ctrl); 293 | dist = HW_getDistanceFromWatchpoint(addr, wp->attr.addr, &ctrl); 294 | if (dist < min_dist) 295 | { 296 | min_dist = dist; 297 | closest_match = i; 298 | } 299 | /* Is this an exact match? */ 300 | if (dist != 0) 301 | continue; 302 | info = HW_counterArchbp(wp); 303 | info->trigger = addr; 304 | closest_match = i; 305 | } 306 | if (min_dist > 0 && min_dist != -1) 307 | { 308 | /* No exact match found. */ 309 | wp = slots[closest_match]; 310 | info = HW_counterArchbp(wp); 311 | info->trigger = addr; 312 | } 313 | rcu_read_unlock(); 314 | 315 | /*关闭所有断点*/ 316 | HW_toggleBpRegisters(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL0, 0); 317 | HW_toggleBpRegisters(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 0); 318 | kernel_step = this_cpu_ptr(&stepping_kernel_bp); 319 | 320 | // printk("watchpoint is trigger,addr=0x%lx, close = %d, dist = %d, mindist = %d, info = %lx\n", 321 | // addr, closest_match, dist, min_dist); 322 | if (info) 323 | { 324 | wp = container_of(info, struct HW_breakpointInfo, info); 325 | if (addr >= wp->attr.addr && addr < wp->attr.addr + wp->attr.len) 326 | { 327 | /*在期望检测的地址范围之内,才打印堆栈信息*/ 328 | printk("wp is triger = 0x%llx, addr = 0x%llx, len = %d\n", addr, wp->attr.addr, wp->attr.len); 329 | kernelApi.fun.show_regs(regs); 330 | } 331 | info->trigger = 0; 332 | } 333 | 334 | if (*kernel_step != ARM_KERNEL_STEP_NONE) 335 | return 0; 336 | 337 | if (kernelApi.fun.kernel_active_single_step()) 338 | { 339 | *kernel_step = ARM_KERNEL_STEP_SUSPEND; 340 | } 341 | else 342 | { 343 | *kernel_step = ARM_KERNEL_STEP_ACTIVE; 344 | /*在当前regs触发step异常*/ 345 | kernelApi.fun.kernel_enable_single_step(regs); 346 | } 347 | // } 348 | 349 | return 0; 350 | } 351 | ``` 352 | 353 | ## 注册调试异常服务函数 354 | 355 | 当断点触发时,是会进入到内核的调试异常服务函数,这个函数里会解析异常类型,去运行对应的断点调试异常函数。内核默认的断点调试异常函数是 `do_bad`,这是一个空函数。需要将这个函数替换为我们自己对应的服务函数。 356 | 357 | ```c 358 | /* 注册调试异常回调函数 */ 359 | /*执行断点*/ 360 | /*保存原先的变量*/ 361 | kernelApi.val.default_fault_info[0].fn = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].fn; 362 | kernelApi.val.default_fault_info[0].sig = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].sig; 363 | kernelApi.val.default_fault_info[0].code = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].code; 364 | kernelApi.val.default_fault_info[0].name = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].name; 365 | /*注册新的内容*/ 366 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].fn = HW_breakpointHandler; 367 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].sig = SIGTRAP; 368 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].code = TRAP_HWBKPT; 369 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWBP].name = "hw-breakpoint handler"; 370 | /*内存断点*/ 371 | /*保存原先的变量*/ 372 | kernelApi.val.default_fault_info[1].fn = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].fn; 373 | kernelApi.val.default_fault_info[1].sig = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].sig; 374 | kernelApi.val.default_fault_info[1].code = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].code; 375 | kernelApi.val.default_fault_info[1].name = kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].name; 376 | /*注册新的内容*/ 377 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].fn = HW_watchpointHandler; 378 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].sig = SIGTRAP; 379 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].code = TRAP_HWBKPT; 380 | kernelApi.val.debug_fault_info[DBG_ESR_EVT_HWWP].name = "hw-watchpoint handler"; 381 | kernelApi.fun.register_step_hook(&gHwStepHook); 382 | 383 | #ifdef CONFIG_CPU_PM 384 | /*注册cpu暂停后恢复断点的回调函数*/ 385 | /*保存原先的变量*/ 386 | kernelApi.val.default_hw_breakpoint_restore = *kernelApi.val.hw_breakpoint_restore; 387 | *kernelApi.val.hw_breakpoint_restore = (u64)HW_breakpointReset; 388 | #endif 389 | ``` 390 | 391 | # 已实现的硬件断点驱动 392 | 393 | 目前的话,该驱动已经实现。详情可参考[硬件断点仓库](https://gitee.com/SmartSmallBoy/hardware-breakpoint)。 394 | 395 | 目前只依赖两个内核选项: 396 | 397 | 1. `CONFIG_KALLSYMS=y` 398 | 2. `CONFIG_KALLSYMS_ALL=y` 399 | 400 | linux 4.19~6.0内核均已测试,可以直接使用。 401 | 402 | 该驱动共分为3部分,分为底层实现,多核CPU的断点同步管理,API接口开放。 403 | 404 | ## 底层实现 405 | 406 | 底层实现包括设置对应的硬件断点寄存器,监控设置的地址,注册调试异常 `hook`等,注册单步异常 `hook`。 407 | 408 | ## 多核心CPU的断点同步管理 409 | 410 | 上述的底层实现只是实现单核 `CPU`断点的设置。假如在一个多核心的 `CPU`上要监视某个内存地址的读写行为,那肯定是要监控所有 `CPU`对该地址的读写行为,只监控当前 `CPU`的访问肯定是不够的。所以该部分是对上述底层的封装,对所有运行的核心进行遍历,通过多核同步机制(`smp_call_function_single`),给所有核心打上/删除相同的断点。 411 | 412 | ## API接口开放 413 | 414 | 该部分集中管理所有通过API接口设置的断点,对外开放设置断点的接口,可选择和 `KGDB`联动,实现断点触发后的源码级调试。分为如下几个部分: 415 | 416 | 1. 直接通过地址设置断点。 417 | 2. 通过符号名字设置断点。 418 | 3. 在启用 `kgdb`联动功能后,可以通过 `kgdb`的设置断点功能设置硬件断点。 419 | 4. 在 `shell`下通过 `proc`调试文件来设置断点。 420 | 5. 查询符号地址。 421 | 6. 查询符号地址里的内容(针对指针变量使用)。 422 | 423 | 断点监测的虚拟地址范围最大为2Gb。当断点被触发时,会打印出当时操作这块内存的堆栈信息,便于问题定位,或是代码的深度理解。若是启用了 `KGDB`的联动功能,则会在打出堆栈信息后,进入 `KGDB`状态,等待远程主机连接调试。`KGDB`的原来及使用可以参考我的前一篇文章[KGDB原理分析及远程挂载调试ARM64内核](https://gitee.com/link?target=https%3A%2F%2Fblog.csdn.net%2Fqq_38384263%2Farticle%2Fdetails%2F132290737%3Fspm%3D1001.2014.3001.5502)。 424 | 425 | -------------------------------------------------------------------------------- /.vscode/compile_commands.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "arguments": [ 4 | "aarch64-linux-gnu-gcc", 5 | "-c", 6 | "-Wp,-MD,/home/zwf/x3_src/test/hardware-breakpoint/.hw_break.mod.o.d", 7 | "-nostdinc", 8 | "-isystem", 9 | "/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc-cross/aarch64-linux-gnu/9/include", 10 | "-I./arch/arm64/include", 11 | "-I./arch/arm64/include/generated", 12 | "-I./include", 13 | "-I./arch/arm64/include/uapi", 14 | "-I./arch/arm64/include/generated/uapi", 15 | "-I./include/uapi", 16 | "-I./include/generated/uapi", 17 | "-include", 18 | "./include/linux/kconfig.h", 19 | "-D__KERNEL__", 20 | "-mlittle-endian", 21 | "-march=armv8-a+crc", 22 | "-Wall", 23 | "-Wundef", 24 | "-Wstrict-prototypes", 25 | "-Wno-trigraphs", 26 | "-fno-strict-aliasing", 27 | "-fno-common", 28 | "-fshort-wchar", 29 | "-Werror-implicit-function-declaration", 30 | "-Wno-format-security", 31 | "-std=gnu89", 32 | "-fno-PIE", 33 | "-mgeneral-regs-only", 34 | "-DCONFIG_AS_LSE=1", 35 | "-fno-asynchronous-unwind-tables", 36 | "-mpc-relative-literal-loads", 37 | "-mabi=lp64", 38 | "-fno-delete-null-pointer-checks", 39 | "-Wno-frame-address", 40 | "-Wno-format-truncation", 41 | "-Wno-format-overflow", 42 | "-Wno-int-in-bool-context", 43 | "-Wno-attribute-alias", 44 | "-O2", 45 | "--param=allow-store-data-races=0", 46 | "-DCC_HAVE_ASM_GOTO", 47 | "-Wframe-larger-than=2048", 48 | "-fno-stack-protector", 49 | "-Wno-unused-but-set-variable", 50 | "-Wno-unused-const-variable", 51 | "-fno-omit-frame-pointer", 52 | "-fno-optimize-sibling-calls", 53 | "-fno-var-tracking-assignments", 54 | "-g", 55 | "-Wdeclaration-after-statement", 56 | "-Wno-pointer-sign", 57 | "-Wno-stringop-truncation", 58 | "-fno-strict-overflow", 59 | "-fno-merge-all-constants", 60 | "-fmerge-constants", 61 | "-fno-stack-check", 62 | "-fconserve-stack", 63 | "-Werror=implicit-int", 64 | "-Werror=strict-prototypes", 65 | "-Werror=date-time", 66 | "-Werror=incompatible-pointer-types", 67 | "-Werror=designated-init", 68 | "-Wno-packed-not-aligned", 69 | "-I/home/zwf/x3_src/test/hardware-breakpoint/include", 70 | "-g", 71 | "-DKBUILD_BASENAME=\"hw_break.mod\"", 72 | "-DKBUILD_MODNAME=\"hw_break\"", 73 | "-DMODULE", 74 | "-mcmodel=large", 75 | "-o", 76 | "/home/zwf/x3_src/test/hardware-breakpoint/hw_break.mod.o", 77 | "../test/hardware-breakpoint/hw_break.mod.c" 78 | ], 79 | "directory": "/home/zwf/x3_src/kernel", 80 | "file": "../test/hardware-breakpoint/hw_break.mod.c" 81 | }, 82 | { 83 | "arguments": [ 84 | "aarch64-linux-gnu-gcc", 85 | "-c", 86 | "-Wp,-MD,/home/zwf/x3_src/test/hardware-breakpoint/src/.hw_breakpointManage.o.d", 87 | "-nostdinc", 88 | "-isystem", 89 | "/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc-cross/aarch64-linux-gnu/9/include", 90 | "-I./arch/arm64/include", 91 | "-I./arch/arm64/include/generated", 92 | "-I./include", 93 | "-I./arch/arm64/include/uapi", 94 | "-I./arch/arm64/include/generated/uapi", 95 | "-I./include/uapi", 96 | "-I./include/generated/uapi", 97 | "-include", 98 | "./include/linux/kconfig.h", 99 | "-D__KERNEL__", 100 | "-mlittle-endian", 101 | "-march=armv8-a+crc", 102 | "-Wall", 103 | "-Wundef", 104 | "-Wstrict-prototypes", 105 | "-Wno-trigraphs", 106 | "-fno-strict-aliasing", 107 | "-fno-common", 108 | "-fshort-wchar", 109 | "-Werror-implicit-function-declaration", 110 | "-Wno-format-security", 111 | "-std=gnu89", 112 | "-fno-PIE", 113 | "-mgeneral-regs-only", 114 | "-DCONFIG_AS_LSE=1", 115 | "-fno-asynchronous-unwind-tables", 116 | "-mpc-relative-literal-loads", 117 | "-mabi=lp64", 118 | "-fno-delete-null-pointer-checks", 119 | "-Wno-frame-address", 120 | "-Wno-format-truncation", 121 | "-Wno-format-overflow", 122 | "-Wno-int-in-bool-context", 123 | "-Wno-attribute-alias", 124 | "-O2", 125 | "--param=allow-store-data-races=0", 126 | "-DCC_HAVE_ASM_GOTO", 127 | "-Wframe-larger-than=2048", 128 | "-fno-stack-protector", 129 | "-Wno-unused-but-set-variable", 130 | "-Wno-unused-const-variable", 131 | "-fno-omit-frame-pointer", 132 | "-fno-optimize-sibling-calls", 133 | "-fno-var-tracking-assignments", 134 | "-g", 135 | "-Wdeclaration-after-statement", 136 | "-Wno-pointer-sign", 137 | "-Wno-stringop-truncation", 138 | "-fno-strict-overflow", 139 | "-fno-merge-all-constants", 140 | "-fmerge-constants", 141 | "-fno-stack-check", 142 | "-fconserve-stack", 143 | "-Werror=implicit-int", 144 | "-Werror=strict-prototypes", 145 | "-Werror=date-time", 146 | "-Werror=incompatible-pointer-types", 147 | "-Werror=designated-init", 148 | "-Wno-packed-not-aligned", 149 | "-I/home/zwf/x3_src/test/hardware-breakpoint/include", 150 | "-g", 151 | "-DMODULE", 152 | "-mcmodel=large", 153 | "-DKBUILD_BASENAME=\"hw_breakpointManage\"", 154 | "-DKBUILD_MODNAME=\"hw_break\"", 155 | "-o", 156 | "/home/zwf/x3_src/test/hardware-breakpoint/src/hw_breakpointManage.o", 157 | "../test/hardware-breakpoint/src/hw_breakpointManage.c" 158 | ], 159 | "directory": "/home/zwf/x3_src/kernel", 160 | "file": "../test/hardware-breakpoint/src/hw_breakpointManage.c" 161 | }, 162 | { 163 | "arguments": [ 164 | "aarch64-linux-gnu-gcc", 165 | "-c", 166 | "-Wp,-MD,/home/zwf/x3_src/test/hardware-breakpoint/src/.hw_proc.o.d", 167 | "-nostdinc", 168 | "-isystem", 169 | "/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc-cross/aarch64-linux-gnu/9/include", 170 | "-I./arch/arm64/include", 171 | "-I./arch/arm64/include/generated", 172 | "-I./include", 173 | "-I./arch/arm64/include/uapi", 174 | "-I./arch/arm64/include/generated/uapi", 175 | "-I./include/uapi", 176 | "-I./include/generated/uapi", 177 | "-include", 178 | "./include/linux/kconfig.h", 179 | "-D__KERNEL__", 180 | "-mlittle-endian", 181 | "-march=armv8-a+crc", 182 | "-Wall", 183 | "-Wundef", 184 | "-Wstrict-prototypes", 185 | "-Wno-trigraphs", 186 | "-fno-strict-aliasing", 187 | "-fno-common", 188 | "-fshort-wchar", 189 | "-Werror-implicit-function-declaration", 190 | "-Wno-format-security", 191 | "-std=gnu89", 192 | "-fno-PIE", 193 | "-mgeneral-regs-only", 194 | "-DCONFIG_AS_LSE=1", 195 | "-fno-asynchronous-unwind-tables", 196 | "-mpc-relative-literal-loads", 197 | "-mabi=lp64", 198 | "-fno-delete-null-pointer-checks", 199 | "-Wno-frame-address", 200 | "-Wno-format-truncation", 201 | "-Wno-format-overflow", 202 | "-Wno-int-in-bool-context", 203 | "-Wno-attribute-alias", 204 | "-O2", 205 | "--param=allow-store-data-races=0", 206 | "-DCC_HAVE_ASM_GOTO", 207 | "-Wframe-larger-than=2048", 208 | "-fno-stack-protector", 209 | "-Wno-unused-but-set-variable", 210 | "-Wno-unused-const-variable", 211 | "-fno-omit-frame-pointer", 212 | "-fno-optimize-sibling-calls", 213 | "-fno-var-tracking-assignments", 214 | "-g", 215 | "-Wdeclaration-after-statement", 216 | "-Wno-pointer-sign", 217 | "-Wno-stringop-truncation", 218 | "-fno-strict-overflow", 219 | "-fno-merge-all-constants", 220 | "-fmerge-constants", 221 | "-fno-stack-check", 222 | "-fconserve-stack", 223 | "-Werror=implicit-int", 224 | "-Werror=strict-prototypes", 225 | "-Werror=date-time", 226 | "-Werror=incompatible-pointer-types", 227 | "-Werror=designated-init", 228 | "-Wno-packed-not-aligned", 229 | "-I/home/zwf/x3_src/test/hardware-breakpoint/include", 230 | "-g", 231 | "-DMODULE", 232 | "-mcmodel=large", 233 | "-DKBUILD_BASENAME=\"hw_proc\"", 234 | "-DKBUILD_MODNAME=\"hw_break\"", 235 | "-o", 236 | "/home/zwf/x3_src/test/hardware-breakpoint/src/hw_proc.o", 237 | "../test/hardware-breakpoint/src/hw_proc.c" 238 | ], 239 | "directory": "/home/zwf/x3_src/kernel", 240 | "file": "../test/hardware-breakpoint/src/hw_proc.c" 241 | }, 242 | { 243 | "arguments": [ 244 | "aarch64-linux-gnu-gcc", 245 | "-c", 246 | "-Wp,-MD,/home/zwf/x3_src/test/hardware-breakpoint/.hw_breakpoint.o.d", 247 | "-nostdinc", 248 | "-isystem", 249 | "/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc-cross/aarch64-linux-gnu/9/include", 250 | "-I./arch/arm64/include", 251 | "-I./arch/arm64/include/generated", 252 | "-I./include", 253 | "-I./arch/arm64/include/uapi", 254 | "-I./arch/arm64/include/generated/uapi", 255 | "-I./include/uapi", 256 | "-I./include/generated/uapi", 257 | "-include", 258 | "./include/linux/kconfig.h", 259 | "-D__KERNEL__", 260 | "-mlittle-endian", 261 | "-march=armv8-a+crc", 262 | "-Wall", 263 | "-Wundef", 264 | "-Wstrict-prototypes", 265 | "-Wno-trigraphs", 266 | "-fno-strict-aliasing", 267 | "-fno-common", 268 | "-fshort-wchar", 269 | "-Werror-implicit-function-declaration", 270 | "-Wno-format-security", 271 | "-std=gnu89", 272 | "-fno-PIE", 273 | "-mgeneral-regs-only", 274 | "-DCONFIG_AS_LSE=1", 275 | "-fno-asynchronous-unwind-tables", 276 | "-mpc-relative-literal-loads", 277 | "-mabi=lp64", 278 | "-fno-delete-null-pointer-checks", 279 | "-Wno-frame-address", 280 | "-Wno-format-truncation", 281 | "-Wno-format-overflow", 282 | "-Wno-int-in-bool-context", 283 | "-Wno-attribute-alias", 284 | "-O2", 285 | "--param=allow-store-data-races=0", 286 | "-DCC_HAVE_ASM_GOTO", 287 | "-Wframe-larger-than=2048", 288 | "-fno-stack-protector", 289 | "-Wno-unused-but-set-variable", 290 | "-Wno-unused-const-variable", 291 | "-fno-omit-frame-pointer", 292 | "-fno-optimize-sibling-calls", 293 | "-fno-var-tracking-assignments", 294 | "-g", 295 | "-Wdeclaration-after-statement", 296 | "-Wno-pointer-sign", 297 | "-Wno-stringop-truncation", 298 | "-fno-strict-overflow", 299 | "-fno-merge-all-constants", 300 | "-fmerge-constants", 301 | "-fno-stack-check", 302 | "-fconserve-stack", 303 | "-Werror=implicit-int", 304 | "-Werror=strict-prototypes", 305 | "-Werror=date-time", 306 | "-Werror=incompatible-pointer-types", 307 | "-Werror=designated-init", 308 | "-Wno-packed-not-aligned", 309 | "-I/home/zwf/x3_src/test/hardware-breakpoint/include", 310 | "-g", 311 | "-DMODULE", 312 | "-mcmodel=large", 313 | "-DKBUILD_BASENAME=\"hw_breakpoint\"", 314 | "-DKBUILD_MODNAME=\"hw_break\"", 315 | "-o", 316 | "/home/zwf/x3_src/test/hardware-breakpoint/hw_breakpoint.o", 317 | "../test/hardware-breakpoint/hw_breakpoint.c" 318 | ], 319 | "directory": "/home/zwf/x3_src/kernel", 320 | "file": "../test/hardware-breakpoint/hw_breakpoint.c" 321 | }, 322 | { 323 | "arguments": [ 324 | "aarch64-linux-gnu-gcc", 325 | "-c", 326 | "-Wp,-MD,/home/zwf/x3_src/test/hardware-breakpoint/src/.hw_breakpointApi.o.d", 327 | "-nostdinc", 328 | "-isystem", 329 | "/opt/gcc-ubuntu-9.3.0-2020.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc-cross/aarch64-linux-gnu/9/include", 330 | "-I./arch/arm64/include", 331 | "-I./arch/arm64/include/generated", 332 | "-I./include", 333 | "-I./arch/arm64/include/uapi", 334 | "-I./arch/arm64/include/generated/uapi", 335 | "-I./include/uapi", 336 | "-I./include/generated/uapi", 337 | "-include", 338 | "./include/linux/kconfig.h", 339 | "-D__KERNEL__", 340 | "-mlittle-endian", 341 | "-march=armv8-a+crc", 342 | "-Wall", 343 | "-Wundef", 344 | "-Wstrict-prototypes", 345 | "-Wno-trigraphs", 346 | "-fno-strict-aliasing", 347 | "-fno-common", 348 | "-fshort-wchar", 349 | "-Werror-implicit-function-declaration", 350 | "-Wno-format-security", 351 | "-std=gnu89", 352 | "-fno-PIE", 353 | "-mgeneral-regs-only", 354 | "-DCONFIG_AS_LSE=1", 355 | "-fno-asynchronous-unwind-tables", 356 | "-mpc-relative-literal-loads", 357 | "-mabi=lp64", 358 | "-fno-delete-null-pointer-checks", 359 | "-Wno-frame-address", 360 | "-Wno-format-truncation", 361 | "-Wno-format-overflow", 362 | "-Wno-int-in-bool-context", 363 | "-Wno-attribute-alias", 364 | "-O2", 365 | "--param=allow-store-data-races=0", 366 | "-DCC_HAVE_ASM_GOTO", 367 | "-Wframe-larger-than=2048", 368 | "-fno-stack-protector", 369 | "-Wno-unused-but-set-variable", 370 | "-Wno-unused-const-variable", 371 | "-fno-omit-frame-pointer", 372 | "-fno-optimize-sibling-calls", 373 | "-fno-var-tracking-assignments", 374 | "-g", 375 | "-Wdeclaration-after-statement", 376 | "-Wno-pointer-sign", 377 | "-Wno-stringop-truncation", 378 | "-fno-strict-overflow", 379 | "-fno-merge-all-constants", 380 | "-fmerge-constants", 381 | "-fno-stack-check", 382 | "-fconserve-stack", 383 | "-Werror=implicit-int", 384 | "-Werror=strict-prototypes", 385 | "-Werror=date-time", 386 | "-Werror=incompatible-pointer-types", 387 | "-Werror=designated-init", 388 | "-Wno-packed-not-aligned", 389 | "-I/home/zwf/x3_src/test/hardware-breakpoint/include", 390 | "-g", 391 | "-DMODULE", 392 | "-mcmodel=large", 393 | "-DKBUILD_BASENAME=\"hw_breakpointApi\"", 394 | "-DKBUILD_MODNAME=\"hw_break\"", 395 | "-o", 396 | "/home/zwf/x3_src/test/hardware-breakpoint/src/hw_breakpointApi.o", 397 | "../test/hardware-breakpoint/src/hw_breakpointApi.c" 398 | ], 399 | "directory": "/home/zwf/x3_src/kernel", 400 | "file": "../test/hardware-breakpoint/src/hw_breakpointApi.c" 401 | } 402 | ] -------------------------------------------------------------------------------- /hw_breakpoint_manage.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "ext_hw_breakpoint.h" 7 | 8 | /*func extern*/ 9 | extern int hw_bp_register(struct hw_bp_info *__percpu *cpu_events, 10 | hw_bp_attr *attr, int *state); 11 | extern void hw_bp_unregister(struct hw_bp_info *__percpu *bp, int state); 12 | 13 | struct hw_bp_manage_info { 14 | struct hw_bp_info **info; /*percpu bp info*/ 15 | hw_bp_attr attr; /*bp attr*/ 16 | int mask; /*bp register cpu mask*/ 17 | char symbol_name[KSYM_SYMBOL_LEN]; /*symbol name of addr*/ 18 | }; 19 | struct hw_bp_manage { 20 | struct hw_bp_manage_info wp[ARM_MAX_WRP]; /*wp*/ 21 | struct hw_bp_manage_info bp[ARM_MAX_BRP]; /*bp*/ 22 | int max_wp_num; /*max num of wp*/ 23 | int max_bp_num; /*max num of bp*/ 24 | int cpu_mask; /*cpu mask, num of cpu*/ 25 | int cpu_num; /**/ 26 | struct mutex lock; /*mutex lock*/ 27 | } __aligned(512); 28 | 29 | static struct hw_bp_manage g_hw_manage; 30 | const char bp_type_str[4][30] = { "HW_BREAKPOINT_R", "HW_BREAKPOINT_W", 31 | "HW_BREAKPOINT_RW", "HW_BREAKPOINT_X" }; 32 | 33 | /*show info of bp*/ 34 | static void hw_bp_show_one(struct hw_bp_manage_info *bp_info, int index) 35 | { 36 | int cpu; 37 | struct hw_bp_info *bp_percpu; 38 | 39 | pr_info("--------------------------------------------------\n"); 40 | /*index of bp*/ 41 | switch (bp_info->attr.type) { 42 | case HW_BREAKPOINT_R: 43 | case HW_BREAKPOINT_W: 44 | case HW_BREAKPOINT_RW: 45 | case HW_BREAKPOINT_X: { 46 | pr_info("breakpoint[%d]:\n", index); 47 | break; 48 | } 49 | default: { 50 | pr_info("breakpoint[%d] type is error!\n", index); 51 | return; 52 | } 53 | } 54 | 55 | /*bp type*/ 56 | pr_info("\ttype: \t%s\n", bp_type_str[bp_info->attr.type - 1]); 57 | /*symbol name of addr*/ 58 | pr_info("\tname: \t%s\n", bp_info->symbol_name); 59 | /*the range of detect*/ 60 | pr_info("\tmonit: \t0x%llx--->0x%llx\n", bp_info->attr.addr, 61 | bp_info->attr.addr + bp_info->attr.len - 1); 62 | /*detect len*/ 63 | pr_info("\tlen: \t%llu\n", bp_info->attr.len); 64 | /*addr mask*/ 65 | pr_info("\tmask: \t0x%x\n", bp_info->attr.mask); 66 | /*the fact of detect range*/ 67 | pr_info("\trange: \t0x%llx--->0x%llx\n", bp_info->attr.start_addr, 68 | bp_info->attr.end_addr); 69 | pr_info("\tsize: \t%llu\n", 70 | bp_info->attr.end_addr - bp_info->attr.start_addr); 71 | pr_info("\ttimes:\n"); 72 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 73 | cpus_read_lock(); 74 | #else 75 | get_online_cpus(); 76 | #endif 77 | for_each_possible_cpu(cpu) { 78 | if (bp_info->mask & 1 << cpu) { 79 | bp_percpu = per_cpu(*bp_info->info, cpu); 80 | pr_info("\t\tcpu[%d]: \tread: %llu, write: %llu, exec: %llu\n", 81 | cpu, bp_percpu->attr.times.read, 82 | bp_percpu->attr.times.write, 83 | bp_percpu->attr.times.exec); 84 | } 85 | } 86 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 87 | cpus_read_unlock(); 88 | #else 89 | put_online_cpus(); 90 | #endif 91 | } 92 | 93 | /*show all bp info*/ 94 | void hw_bp_show_all(void) 95 | { 96 | struct hw_bp_manage_info *bp_info = NULL; 97 | int i = 0; 98 | 99 | mutex_lock(&g_hw_manage.lock); 100 | for (i = 0; i < g_hw_manage.max_bp_num; i++) { 101 | bp_info = &g_hw_manage.bp[i]; 102 | if (bp_info->mask & g_hw_manage.cpu_mask) { 103 | hw_bp_show_one(bp_info, i); 104 | } 105 | } 106 | 107 | for (i = 0; i < g_hw_manage.max_wp_num; i++) { 108 | bp_info = &g_hw_manage.wp[i]; 109 | if (bp_info->mask & g_hw_manage.cpu_mask) { 110 | hw_bp_show_one(bp_info, i + g_hw_manage.max_bp_num); 111 | } 112 | } 113 | mutex_unlock(&g_hw_manage.lock); 114 | } 115 | 116 | static void hw_bp_uninstall_all(void) 117 | { 118 | struct hw_bp_manage_info *bp_info = NULL; 119 | int i = 0; 120 | 121 | mutex_lock(&g_hw_manage.lock); 122 | for (i = 0; i < g_hw_manage.max_bp_num; i++) { 123 | bp_info = &g_hw_manage.bp[i]; 124 | if (bp_info->mask & g_hw_manage.cpu_mask) { 125 | hw_bp_unregister(bp_info->info, bp_info->mask); 126 | /*clear info*/ 127 | memset(bp_info->symbol_name, 0, 128 | sizeof(bp_info->symbol_name)); 129 | memset(&bp_info->attr, 0, sizeof(bp_info->attr)); 130 | bp_info->mask = 0; 131 | } 132 | } 133 | 134 | for (i = 0; i < g_hw_manage.max_wp_num; i++) { 135 | bp_info = &g_hw_manage.wp[i]; 136 | if (bp_info->mask & g_hw_manage.cpu_mask) { 137 | hw_bp_unregister(bp_info->info, bp_info->mask); 138 | /*clear info*/ 139 | memset(bp_info->symbol_name, 0, 140 | sizeof(bp_info->symbol_name)); 141 | memset(&bp_info->attr, 0, sizeof(bp_info->attr)); 142 | bp_info->mask = 0; 143 | } 144 | } 145 | mutex_unlock(&g_hw_manage.lock); 146 | } 147 | 148 | static int hw_get_addr_mask(u64 addr, int len) 149 | { 150 | /*end of the detect addr*/ 151 | u64 addr_tmp = addr + len; 152 | u64 alignment_mask = 0; 153 | int mask, i = 0; 154 | 155 | /*log2(len)*/ 156 | mask = (int)__ilog2_u64(len); 157 | if ((1 << mask) < len) { 158 | mask = mask + 1; 159 | } 160 | for (i = 0; i < mask; i++) { 161 | alignment_mask |= (1 << i); 162 | } 163 | 164 | /*Confirm that the end address is within the actual monitoring range*/ 165 | while (1) { 166 | if ((addr | alignment_mask) >= addr_tmp) { 167 | break; 168 | } 169 | mask = mask + 1; 170 | alignment_mask |= (1 << i); 171 | i++; 172 | } 173 | 174 | if (mask > 31) { 175 | /*arm64 the mask is 0b11111*/ 176 | mask = 31; 177 | } 178 | return mask; 179 | } 180 | 181 | static void hw_bp_handler_default(const hw_bp_callback_data *info, 182 | const struct pt_regs *regs) 183 | { 184 | pr_info("bp is triger = 0x%llx, type = %s\n", info->addr, 185 | bp_type_str[info->type - 1]); 186 | pr_info("times: read=%llu, write=%llu, exec=%llu\n", info->times.read, 187 | info->times.write, info->times.exec); 188 | HW_SYMS_FUNC(show_regs)((struct pt_regs *)regs); 189 | } 190 | 191 | /*install bp from addr*/ 192 | int hw_bp_install_from_addr(u64 addr, int len, int type, hw_bp_callback handler) 193 | { 194 | int state, i, max_num, ret, mask = 0; 195 | struct hw_bp_manage_info *bp_info; 196 | u64 start_addr, end_addr; 197 | u64 alignment_mask = 0, real_len = len, offset; 198 | 199 | if ((0 == addr) || (addr < TASK_SIZE)) { 200 | pr_info("hw_bp_install_from_addr para is error\n"); 201 | return -1; 202 | } 203 | 204 | switch (type) { 205 | case HW_BREAKPOINT_R: 206 | case HW_BREAKPOINT_W: 207 | case HW_BREAKPOINT_RW: { 208 | /*wp*/ 209 | bp_info = g_hw_manage.wp; 210 | max_num = g_hw_manage.max_wp_num; 211 | if (len > 8) { 212 | /*len>8, use mask*/ 213 | mask = hw_get_addr_mask(addr, len); 214 | real_len = 4; 215 | } 216 | if (mask != 0) { 217 | /*get mask startaddr&endaddr*/ 218 | for (i = 0; i < mask; i++) { 219 | alignment_mask |= (1 << i); 220 | } 221 | start_addr = addr & ~(alignment_mask); 222 | end_addr = addr | alignment_mask; 223 | } else { 224 | /*len<=8, use LBN*/ 225 | alignment_mask = 0x7; 226 | offset = addr & alignment_mask; 227 | real_len = len << offset; 228 | if (real_len > 8) { 229 | real_len = 8; 230 | } 231 | start_addr = addr & ~(alignment_mask); 232 | end_addr = start_addr + real_len; 233 | } 234 | break; 235 | } 236 | case HW_BREAKPOINT_X: { 237 | /*bp*/ 238 | real_len = 4; 239 | bp_info = g_hw_manage.bp; 240 | max_num = g_hw_manage.max_bp_num; 241 | alignment_mask = 0x3; 242 | offset = addr & alignment_mask; 243 | real_len = len << offset; 244 | if (real_len > 8) { 245 | real_len = 8; 246 | } 247 | start_addr = addr & ~(alignment_mask); 248 | end_addr = start_addr + real_len; 249 | break; 250 | } 251 | default: { 252 | /*bp type error*/ 253 | pr_info("breakpoint type error\n"); 254 | return -1; 255 | } 256 | } 257 | 258 | mutex_lock(&g_hw_manage.lock); 259 | for (i = 0; i < max_num; i++) { 260 | if ((bp_info[i].mask & g_hw_manage.cpu_mask) != 0) { 261 | /*This bp has been set*/ 262 | if (bp_info[i].attr.addr == addr) { 263 | pr_info("[install] The addr [%llx] is already set at index %d\n", 264 | addr, i); 265 | mutex_unlock(&g_hw_manage.lock); 266 | return -1; 267 | } 268 | } 269 | } 270 | 271 | for (i = 0; i < max_num; i++) { 272 | if ((bp_info[i].mask & g_hw_manage.cpu_mask) != 0) { 273 | continue; 274 | } 275 | bp_info[i].attr.len = len; 276 | bp_info[i].attr.real_len = real_len; 277 | bp_info[i].attr.mask = mask; 278 | bp_info[i].attr.type = type; 279 | bp_info[i].attr.addr = addr; 280 | bp_info[i].attr.start_addr = start_addr; 281 | bp_info[i].attr.end_addr = end_addr; 282 | bp_info[i].attr.handler = handler; 283 | if (bp_info[i].attr.handler == NULL) { 284 | bp_info[i].attr.handler = hw_bp_handler_default; 285 | } 286 | break; 287 | } 288 | 289 | if (i == max_num) { 290 | pr_info("[install] breakpoint is full type = %x\n", type); 291 | mutex_unlock(&g_hw_manage.lock); 292 | return -1; 293 | } 294 | 295 | // pr_info("gHwManage.wp[%d].info = %lx\n", i, gHwManage.wp[i].info); 296 | // pr_info("info = %lx,attr=%lx,state=%lx\n", bpInfo[i].info, &bpInfo[i].attr, &state); 297 | ret = hw_bp_register(bp_info[i].info, &bp_info[i].attr, &state); 298 | if (ret) { 299 | goto clear; 300 | } 301 | /*Several CPUs are registered with the breakpoint*/ 302 | bp_info[i].mask = state; 303 | memset(bp_info[i].symbol_name, 0, sizeof(bp_info[i].symbol_name)); 304 | sprint_symbol(bp_info[i].symbol_name, addr); 305 | mutex_unlock(&g_hw_manage.lock); 306 | hw_bp_show_one(&bp_info[i], i); 307 | return 0; 308 | clear: 309 | pr_info("hw_bp_install_from_addr [%llx] error\n", addr); 310 | /*clear bp info*/ 311 | memset(&bp_info[i].attr, 0, sizeof(bp_info[i].attr)); 312 | memset(bp_info[i].symbol_name, 0, sizeof(bp_info[i].symbol_name)); 313 | bp_info[i].mask = 0; 314 | mutex_unlock(&g_hw_manage.lock); 315 | return -1; 316 | } 317 | EXPORT_SYMBOL_GPL(hw_bp_install_from_addr); 318 | 319 | /*从符号设置一个断点*/ 320 | int hw_bp_install_from_symbol(char *name, int len, int type, 321 | hw_bp_callback handler) 322 | { 323 | int ret = 0; 324 | u64 addr = 0; 325 | 326 | if ((NULL == name) || (HW_BREAKPOINT_INVALID == type)) { 327 | pr_info("HW_breakpointInstallFromSymbol para is error\n"); 328 | return -1; 329 | } 330 | 331 | addr = HW_SYMS_FUNC(kallsyms_lookup_name)(name); 332 | if (0 == addr) { 333 | /*the symbol is invalid*/ 334 | pr_info("Can not find the symbol, name: %s\n", name); 335 | return -1; 336 | } 337 | 338 | ret = hw_bp_install_from_addr(addr, len, type, handler); 339 | if (ret) { 340 | pr_info("HW_breakpointInstallFromSymbol error [%s]\n", name); 341 | return -1; 342 | } 343 | 344 | return 0; 345 | } 346 | EXPORT_SYMBOL_GPL(hw_bp_install_from_symbol); 347 | 348 | void hw_bp_uninstall_from_addr(u64 addr) 349 | { 350 | int i = 0; 351 | struct hw_bp_manage_info *bp_info = NULL; 352 | 353 | /*traverse bp arrays*/ 354 | /*find bp*/ 355 | mutex_lock(&g_hw_manage.lock); 356 | for (i = 0; i < g_hw_manage.max_bp_num; i++) { 357 | if (g_hw_manage.bp[i].mask & g_hw_manage.cpu_mask) { 358 | if (g_hw_manage.bp[i].attr.addr == addr) { 359 | bp_info = &g_hw_manage.bp[i]; 360 | pr_info("[uninstall] find addr: bp[%d]\n", i); 361 | break; 362 | } 363 | } 364 | } 365 | /*find wp*/ 366 | for (i = 0; (i < g_hw_manage.max_wp_num) && (bp_info == NULL); i++) { 367 | if (g_hw_manage.wp[i].mask & g_hw_manage.cpu_mask) { 368 | if (g_hw_manage.wp[i].attr.addr == addr) { 369 | bp_info = &g_hw_manage.wp[i]; 370 | pr_info("[uninstall] find addr: wp[%d]\n", i); 371 | break; 372 | } 373 | } 374 | } 375 | if (NULL == bp_info) { 376 | pr_info("HW_breakpointUnInstallFromAddr fail,can not find addr:0x%llx\n", 377 | addr); 378 | mutex_unlock(&g_hw_manage.lock); 379 | return; 380 | } 381 | hw_bp_unregister(bp_info->info, bp_info->mask); 382 | /*clear bp info*/ 383 | memset(bp_info->symbol_name, 0, sizeof(bp_info->symbol_name)); 384 | memset(&bp_info->attr, 0, sizeof(bp_info->attr)); 385 | bp_info->mask = 0; 386 | mutex_unlock(&g_hw_manage.lock); 387 | } 388 | EXPORT_SYMBOL_GPL(hw_bp_uninstall_from_addr); 389 | 390 | void hw_bp_uninstall_from_symbol(char *name) 391 | { 392 | u64 addr = 0; 393 | 394 | if (NULL == name) { 395 | pr_info("HW_breakpointUnInstallFromSymbol para is error\n"); 396 | return; 397 | } 398 | 399 | addr = HW_SYMS_FUNC(kallsyms_lookup_name)(name); 400 | if (0 == addr) { 401 | /*the symbol is invalid*/ 402 | pr_info("[uninstall] Can not find the symbol, name: %s\n", 403 | name); 404 | return; 405 | } 406 | hw_bp_uninstall_from_addr(addr); 407 | } 408 | EXPORT_SYMBOL_GPL(hw_bp_uninstall_from_symbol); 409 | 410 | void hw_free_bp_infos(hw_bp_info_list *info) 411 | { 412 | hw_bp_info_list *node = NULL, *next = NULL; 413 | 414 | if (info) { 415 | list_for_each_entry_safe(node, next, &info->list, list) { 416 | list_del(&node->list); 417 | if (node->attr) { 418 | kfree(node->attr); 419 | } 420 | kfree(node); 421 | } 422 | if (info->attr) { 423 | kfree(info->attr); 424 | } 425 | kfree(info); 426 | } 427 | } 428 | EXPORT_SYMBOL_GPL(hw_free_bp_infos); 429 | 430 | static void hw_fill_report_data(struct hw_bp_manage_info *bp_info, 431 | hw_bp_info_list *node) 432 | { 433 | struct hw_bp_info *bp = NULL; 434 | int cpu = 0; 435 | 436 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 437 | cpus_read_lock(); 438 | #else 439 | get_online_cpus(); 440 | #endif 441 | for_each_possible_cpu(cpu) { 442 | if (bp_info->mask & 1 << cpu) { 443 | bp = per_cpu(*bp_info->info, cpu); 444 | /*value*/ 445 | node->attr[cpu].type = bp->attr.type; 446 | node->attr[cpu].addr = bp->attr.addr; 447 | node->attr[cpu].len = bp->attr.len; 448 | node->attr[cpu].mask = bp->attr.mask; 449 | node->attr[cpu].times = bp->attr.times; 450 | } 451 | } 452 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 453 | cpus_read_unlock(); 454 | #else 455 | put_online_cpus(); 456 | #endif 457 | } 458 | 459 | hw_bp_info_list *hw_get_bp_infos(void) 460 | { 461 | hw_bp_info_list *head = NULL; 462 | hw_bp_info_list *node = NULL; 463 | struct hw_bp_manage_info *bp_info = NULL; 464 | int i = 0; 465 | 466 | mutex_lock(&g_hw_manage.lock); 467 | for (i = 0; i < g_hw_manage.max_bp_num; i++) { 468 | bp_info = &g_hw_manage.bp[i]; 469 | if (bp_info->mask & g_hw_manage.cpu_mask) { 470 | /*bp is set*/ 471 | if (head == NULL) { 472 | head = kzalloc(sizeof(hw_bp_info_list), 473 | GFP_KERNEL); 474 | if (head == NULL) { 475 | goto err; 476 | } 477 | INIT_LIST_HEAD(&head->list); 478 | head->attr = 479 | kzalloc(sizeof(hw_bp_report) * 480 | g_hw_manage.cpu_num, 481 | GFP_KERNEL); 482 | if (head->attr == NULL) { 483 | goto err; 484 | } 485 | head->cpu_mask = bp_info->mask; 486 | head->cpu_num = g_hw_manage.cpu_num; 487 | hw_fill_report_data(bp_info, head); 488 | } 489 | node = kzalloc(sizeof(hw_bp_info_list), GFP_KERNEL); 490 | if (node == NULL) { 491 | goto err; 492 | } 493 | INIT_LIST_HEAD(&node->list); 494 | list_add_tail(&node->list, &head->list); 495 | node->attr = kzalloc(sizeof(hw_bp_report) * 496 | g_hw_manage.cpu_num, 497 | GFP_KERNEL); 498 | if (node->attr == NULL) { 499 | goto err; 500 | } 501 | node->cpu_mask = bp_info->mask; 502 | node->cpu_num = g_hw_manage.cpu_num; 503 | hw_fill_report_data(bp_info, node); 504 | } 505 | } 506 | 507 | for (i = 0; i < g_hw_manage.max_wp_num; i++) { 508 | bp_info = &g_hw_manage.wp[i]; 509 | if (bp_info->mask & g_hw_manage.cpu_mask) { 510 | /*bp is set*/ 511 | if (head == NULL) { 512 | head = kzalloc(sizeof(hw_bp_info_list), 513 | GFP_KERNEL); 514 | if (head == NULL) { 515 | goto err; 516 | } 517 | INIT_LIST_HEAD(&head->list); 518 | head->attr = 519 | kzalloc(sizeof(hw_bp_report) * 520 | g_hw_manage.cpu_num, 521 | GFP_KERNEL); 522 | if (head->attr == NULL) { 523 | goto err; 524 | } 525 | head->cpu_mask = bp_info->mask; 526 | head->cpu_num = g_hw_manage.cpu_num; 527 | hw_fill_report_data(bp_info, head); 528 | } 529 | node = kzalloc(sizeof(hw_bp_info_list), GFP_KERNEL); 530 | if (node == NULL) { 531 | goto err; 532 | } 533 | INIT_LIST_HEAD(&node->list); 534 | list_add_tail(&node->list, &head->list); 535 | node->attr = kzalloc(sizeof(hw_bp_report) * 536 | g_hw_manage.cpu_num, 537 | GFP_KERNEL); 538 | if (node->attr == NULL) { 539 | goto err; 540 | } 541 | node->cpu_mask = bp_info->mask; 542 | node->cpu_num = g_hw_manage.cpu_num; 543 | hw_fill_report_data(bp_info, node); 544 | } 545 | } 546 | mutex_unlock(&g_hw_manage.lock); 547 | 548 | return head; 549 | 550 | err: 551 | mutex_unlock(&g_hw_manage.lock); 552 | hw_free_bp_infos(head); 553 | return NULL; 554 | } 555 | EXPORT_SYMBOL_GPL(hw_get_bp_infos); 556 | 557 | /*release bp*/ 558 | void hw_bp_manage_deinit(void) 559 | { 560 | int i = 0; 561 | 562 | hw_bp_uninstall_all(); 563 | 564 | for (i = 0; i < g_hw_manage.max_wp_num; i++) { 565 | free_percpu(g_hw_manage.wp[i].info); 566 | } 567 | 568 | for (i = 0; i < g_hw_manage.max_bp_num; i++) { 569 | free_percpu(g_hw_manage.bp[i].info); 570 | } 571 | mutex_destroy(&g_hw_manage.lock); 572 | } 573 | 574 | /*bp arch init*/ 575 | int hw_bp_manage_init(void) 576 | { 577 | int cpu = -1, i = 0; 578 | struct hw_bp_info *__percpu *bp = NULL; 579 | 580 | /*get bp&wp num*/ 581 | g_hw_manage.max_bp_num = hw_get_bp_num(TYPE_INST); 582 | g_hw_manage.max_wp_num = hw_get_bp_num(TYPE_DATA); 583 | 584 | /*get CPU num*/ 585 | g_hw_manage.cpu_num = 0; 586 | for_each_online_cpu(cpu) { 587 | g_hw_manage.cpu_mask |= 1 << cpu; 588 | g_hw_manage.cpu_num++; 589 | } 590 | pr_info("CPU MASK = %x\n", g_hw_manage.cpu_mask); 591 | 592 | /*mange mem of bp*/ 593 | for (i = 0; i < g_hw_manage.max_wp_num; i++) { 594 | bp = alloc_percpu(typeof(*bp)); 595 | if (!bp) { 596 | pr_info("wp alloc_percpu fail\n"); 597 | goto free; 598 | } 599 | g_hw_manage.wp[i].info = bp; 600 | bp = NULL; 601 | } 602 | for (i = 0; i < g_hw_manage.max_bp_num; i++) { 603 | bp = alloc_percpu(typeof(*bp)); 604 | if (!bp) { 605 | pr_info("wp alloc_percpu fail\n"); 606 | goto free; 607 | } 608 | g_hw_manage.bp[i].info = bp; 609 | bp = NULL; 610 | } 611 | 612 | mutex_init(&g_hw_manage.lock); 613 | 614 | return 0; 615 | 616 | free: 617 | hw_bp_manage_deinit(); 618 | return -1; 619 | } 620 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 2 | # 3 | # clang-format configuration file. Intended for clang-format >= 11. 4 | # 5 | # For more information, see: 6 | # 7 | # Documentation/process/clang-format.rst 8 | # https://clang.llvm.org/docs/ClangFormat.html 9 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 10 | # 11 | --- 12 | AccessModifierOffset: -4 13 | AlignAfterOpenBracket: Align 14 | AlignConsecutiveAssignments: false 15 | AlignConsecutiveDeclarations: false 16 | AlignEscapedNewlines: Left 17 | AlignOperands: true 18 | AlignTrailingComments: false 19 | AllowAllParametersOfDeclarationOnNextLine: false 20 | AllowShortBlocksOnASingleLine: false 21 | AllowShortCaseLabelsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: None 23 | AllowShortIfStatementsOnASingleLine: false 24 | AllowShortLoopsOnASingleLine: false 25 | AlwaysBreakAfterDefinitionReturnType: None 26 | AlwaysBreakAfterReturnType: None 27 | AlwaysBreakBeforeMultilineStrings: false 28 | AlwaysBreakTemplateDeclarations: false 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterClass: false 33 | AfterControlStatement: false 34 | AfterEnum: false 35 | AfterFunction: true 36 | AfterNamespace: true 37 | AfterObjCDeclaration: false 38 | AfterStruct: false 39 | AfterUnion: false 40 | AfterExternBlock: false 41 | BeforeCatch: false 42 | BeforeElse: false 43 | IndentBraces: false 44 | SplitEmptyFunction: true 45 | SplitEmptyRecord: true 46 | SplitEmptyNamespace: true 47 | BreakBeforeBinaryOperators: None 48 | BreakBeforeBraces: Custom 49 | BreakBeforeInheritanceComma: false 50 | BreakBeforeTernaryOperators: false 51 | BreakConstructorInitializersBeforeComma: false 52 | BreakConstructorInitializers: BeforeComma 53 | BreakAfterJavaFieldAnnotations: false 54 | BreakStringLiterals: false 55 | ColumnLimit: 80 56 | CommentPragmas: '^ IWYU pragma:' 57 | CompactNamespaces: false 58 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 59 | ConstructorInitializerIndentWidth: 8 60 | ContinuationIndentWidth: 8 61 | Cpp11BracedListStyle: false 62 | DerivePointerAlignment: false 63 | DisableFormat: false 64 | ExperimentalAutoDetectBinPacking: false 65 | FixNamespaceComments: false 66 | 67 | # Taken from: 68 | # git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ 69 | # | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ 70 | # | LC_ALL=C sort -u 71 | ForEachMacros: 72 | - '__ata_qc_for_each' 73 | - '__bio_for_each_bvec' 74 | - '__bio_for_each_segment' 75 | - '__evlist__for_each_entry' 76 | - '__evlist__for_each_entry_continue' 77 | - '__evlist__for_each_entry_from' 78 | - '__evlist__for_each_entry_reverse' 79 | - '__evlist__for_each_entry_safe' 80 | - '__for_each_mem_range' 81 | - '__for_each_mem_range_rev' 82 | - '__for_each_thread' 83 | - '__hlist_for_each_rcu' 84 | - '__map__for_each_symbol_by_name' 85 | - '__perf_evlist__for_each_entry' 86 | - '__perf_evlist__for_each_entry_reverse' 87 | - '__perf_evlist__for_each_entry_safe' 88 | - '__rq_for_each_bio' 89 | - '__shost_for_each_device' 90 | - 'apei_estatus_for_each_section' 91 | - 'ata_for_each_dev' 92 | - 'ata_for_each_link' 93 | - 'ata_qc_for_each' 94 | - 'ata_qc_for_each_raw' 95 | - 'ata_qc_for_each_with_internal' 96 | - 'ax25_for_each' 97 | - 'ax25_uid_for_each' 98 | - 'bio_for_each_bvec' 99 | - 'bio_for_each_bvec_all' 100 | - 'bio_for_each_folio_all' 101 | - 'bio_for_each_integrity_vec' 102 | - 'bio_for_each_segment' 103 | - 'bio_for_each_segment_all' 104 | - 'bio_list_for_each' 105 | - 'bip_for_each_vec' 106 | - 'bond_for_each_slave' 107 | - 'bond_for_each_slave_rcu' 108 | - 'bpf__perf_for_each_map' 109 | - 'bpf__perf_for_each_map_named' 110 | - 'bpf_for_each_spilled_reg' 111 | - 'bpf_object__for_each_map' 112 | - 'bpf_object__for_each_program' 113 | - 'bpf_object__for_each_safe' 114 | - 'bpf_perf_object__for_each' 115 | - 'btree_for_each_safe128' 116 | - 'btree_for_each_safe32' 117 | - 'btree_for_each_safe64' 118 | - 'btree_for_each_safel' 119 | - 'card_for_each_dev' 120 | - 'cgroup_taskset_for_each' 121 | - 'cgroup_taskset_for_each_leader' 122 | - 'cpufreq_for_each_efficient_entry_idx' 123 | - 'cpufreq_for_each_entry' 124 | - 'cpufreq_for_each_entry_idx' 125 | - 'cpufreq_for_each_valid_entry' 126 | - 'cpufreq_for_each_valid_entry_idx' 127 | - 'css_for_each_child' 128 | - 'css_for_each_descendant_post' 129 | - 'css_for_each_descendant_pre' 130 | - 'damon_for_each_region' 131 | - 'damon_for_each_region_safe' 132 | - 'damon_for_each_scheme' 133 | - 'damon_for_each_scheme_safe' 134 | - 'damon_for_each_target' 135 | - 'damon_for_each_target_safe' 136 | - 'data__for_each_file' 137 | - 'data__for_each_file_new' 138 | - 'data__for_each_file_start' 139 | - 'device_for_each_child_node' 140 | - 'displayid_iter_for_each' 141 | - 'dma_fence_array_for_each' 142 | - 'dma_fence_chain_for_each' 143 | - 'dma_fence_unwrap_for_each' 144 | - 'dma_resv_for_each_fence' 145 | - 'dma_resv_for_each_fence_unlocked' 146 | - 'do_for_each_ftrace_op' 147 | - 'drm_atomic_crtc_for_each_plane' 148 | - 'drm_atomic_crtc_state_for_each_plane' 149 | - 'drm_atomic_crtc_state_for_each_plane_state' 150 | - 'drm_atomic_for_each_plane_damage' 151 | - 'drm_client_for_each_connector_iter' 152 | - 'drm_client_for_each_modeset' 153 | - 'drm_connector_for_each_possible_encoder' 154 | - 'drm_for_each_bridge_in_chain' 155 | - 'drm_for_each_connector_iter' 156 | - 'drm_for_each_crtc' 157 | - 'drm_for_each_crtc_reverse' 158 | - 'drm_for_each_encoder' 159 | - 'drm_for_each_encoder_mask' 160 | - 'drm_for_each_fb' 161 | - 'drm_for_each_legacy_plane' 162 | - 'drm_for_each_plane' 163 | - 'drm_for_each_plane_mask' 164 | - 'drm_for_each_privobj' 165 | - 'drm_mm_for_each_hole' 166 | - 'drm_mm_for_each_node' 167 | - 'drm_mm_for_each_node_in_range' 168 | - 'drm_mm_for_each_node_safe' 169 | - 'dsa_switch_for_each_available_port' 170 | - 'dsa_switch_for_each_cpu_port' 171 | - 'dsa_switch_for_each_port' 172 | - 'dsa_switch_for_each_port_continue_reverse' 173 | - 'dsa_switch_for_each_port_safe' 174 | - 'dsa_switch_for_each_user_port' 175 | - 'dsa_tree_for_each_user_port' 176 | - 'dso__for_each_symbol' 177 | - 'dsos__for_each_with_build_id' 178 | - 'elf_hash_for_each_possible' 179 | - 'elf_section__for_each_rel' 180 | - 'elf_section__for_each_rela' 181 | - 'elf_symtab__for_each_symbol' 182 | - 'evlist__for_each_cpu' 183 | - 'evlist__for_each_entry' 184 | - 'evlist__for_each_entry_continue' 185 | - 'evlist__for_each_entry_from' 186 | - 'evlist__for_each_entry_reverse' 187 | - 'evlist__for_each_entry_safe' 188 | - 'flow_action_for_each' 189 | - 'for_each_acpi_dev_match' 190 | - 'for_each_active_dev_scope' 191 | - 'for_each_active_drhd_unit' 192 | - 'for_each_active_iommu' 193 | - 'for_each_aggr_pgid' 194 | - 'for_each_available_child_of_node' 195 | - 'for_each_bench' 196 | - 'for_each_bio' 197 | - 'for_each_board_func_rsrc' 198 | - 'for_each_btf_ext_rec' 199 | - 'for_each_btf_ext_sec' 200 | - 'for_each_bvec' 201 | - 'for_each_card_auxs' 202 | - 'for_each_card_auxs_safe' 203 | - 'for_each_card_components' 204 | - 'for_each_card_dapms' 205 | - 'for_each_card_pre_auxs' 206 | - 'for_each_card_prelinks' 207 | - 'for_each_card_rtds' 208 | - 'for_each_card_rtds_safe' 209 | - 'for_each_card_widgets' 210 | - 'for_each_card_widgets_safe' 211 | - 'for_each_cgroup_storage_type' 212 | - 'for_each_child_of_node' 213 | - 'for_each_clear_bit' 214 | - 'for_each_clear_bit_from' 215 | - 'for_each_clear_bitrange' 216 | - 'for_each_clear_bitrange_from' 217 | - 'for_each_cmd' 218 | - 'for_each_cmsghdr' 219 | - 'for_each_collection' 220 | - 'for_each_comp_order' 221 | - 'for_each_compatible_node' 222 | - 'for_each_component_dais' 223 | - 'for_each_component_dais_safe' 224 | - 'for_each_console' 225 | - 'for_each_cpu' 226 | - 'for_each_cpu_and' 227 | - 'for_each_cpu_not' 228 | - 'for_each_cpu_wrap' 229 | - 'for_each_dapm_widgets' 230 | - 'for_each_dedup_cand' 231 | - 'for_each_dev_addr' 232 | - 'for_each_dev_scope' 233 | - 'for_each_dma_cap_mask' 234 | - 'for_each_dpcm_be' 235 | - 'for_each_dpcm_be_rollback' 236 | - 'for_each_dpcm_be_safe' 237 | - 'for_each_dpcm_fe' 238 | - 'for_each_drhd_unit' 239 | - 'for_each_dss_dev' 240 | - 'for_each_efi_memory_desc' 241 | - 'for_each_efi_memory_desc_in_map' 242 | - 'for_each_element' 243 | - 'for_each_element_extid' 244 | - 'for_each_element_id' 245 | - 'for_each_endpoint_of_node' 246 | - 'for_each_event' 247 | - 'for_each_event_tps' 248 | - 'for_each_evictable_lru' 249 | - 'for_each_fib6_node_rt_rcu' 250 | - 'for_each_fib6_walker_rt' 251 | - 'for_each_free_mem_pfn_range_in_zone' 252 | - 'for_each_free_mem_pfn_range_in_zone_from' 253 | - 'for_each_free_mem_range' 254 | - 'for_each_free_mem_range_reverse' 255 | - 'for_each_func_rsrc' 256 | - 'for_each_group_evsel' 257 | - 'for_each_group_member' 258 | - 'for_each_hstate' 259 | - 'for_each_if' 260 | - 'for_each_inject_fn' 261 | - 'for_each_insn' 262 | - 'for_each_insn_prefix' 263 | - 'for_each_intid' 264 | - 'for_each_iommu' 265 | - 'for_each_ip_tunnel_rcu' 266 | - 'for_each_irq_nr' 267 | - 'for_each_lang' 268 | - 'for_each_link_codecs' 269 | - 'for_each_link_cpus' 270 | - 'for_each_link_platforms' 271 | - 'for_each_lru' 272 | - 'for_each_matching_node' 273 | - 'for_each_matching_node_and_match' 274 | - 'for_each_mem_pfn_range' 275 | - 'for_each_mem_range' 276 | - 'for_each_mem_range_rev' 277 | - 'for_each_mem_region' 278 | - 'for_each_member' 279 | - 'for_each_memory' 280 | - 'for_each_migratetype_order' 281 | - 'for_each_missing_reg' 282 | - 'for_each_net' 283 | - 'for_each_net_continue_reverse' 284 | - 'for_each_net_rcu' 285 | - 'for_each_netdev' 286 | - 'for_each_netdev_continue' 287 | - 'for_each_netdev_continue_rcu' 288 | - 'for_each_netdev_continue_reverse' 289 | - 'for_each_netdev_feature' 290 | - 'for_each_netdev_in_bond_rcu' 291 | - 'for_each_netdev_rcu' 292 | - 'for_each_netdev_reverse' 293 | - 'for_each_netdev_safe' 294 | - 'for_each_new_connector_in_state' 295 | - 'for_each_new_crtc_in_state' 296 | - 'for_each_new_mst_mgr_in_state' 297 | - 'for_each_new_plane_in_state' 298 | - 'for_each_new_plane_in_state_reverse' 299 | - 'for_each_new_private_obj_in_state' 300 | - 'for_each_new_reg' 301 | - 'for_each_node' 302 | - 'for_each_node_by_name' 303 | - 'for_each_node_by_type' 304 | - 'for_each_node_mask' 305 | - 'for_each_node_state' 306 | - 'for_each_node_with_cpus' 307 | - 'for_each_node_with_property' 308 | - 'for_each_nonreserved_multicast_dest_pgid' 309 | - 'for_each_of_allnodes' 310 | - 'for_each_of_allnodes_from' 311 | - 'for_each_of_cpu_node' 312 | - 'for_each_of_pci_range' 313 | - 'for_each_old_connector_in_state' 314 | - 'for_each_old_crtc_in_state' 315 | - 'for_each_old_mst_mgr_in_state' 316 | - 'for_each_old_plane_in_state' 317 | - 'for_each_old_private_obj_in_state' 318 | - 'for_each_oldnew_connector_in_state' 319 | - 'for_each_oldnew_crtc_in_state' 320 | - 'for_each_oldnew_mst_mgr_in_state' 321 | - 'for_each_oldnew_plane_in_state' 322 | - 'for_each_oldnew_plane_in_state_reverse' 323 | - 'for_each_oldnew_private_obj_in_state' 324 | - 'for_each_online_cpu' 325 | - 'for_each_online_node' 326 | - 'for_each_online_pgdat' 327 | - 'for_each_path' 328 | - 'for_each_pci_bridge' 329 | - 'for_each_pci_dev' 330 | - 'for_each_pcm_streams' 331 | - 'for_each_physmem_range' 332 | - 'for_each_populated_zone' 333 | - 'for_each_possible_cpu' 334 | - 'for_each_present_cpu' 335 | - 'for_each_prime_number' 336 | - 'for_each_prime_number_from' 337 | - 'for_each_probe_cache_entry' 338 | - 'for_each_process' 339 | - 'for_each_process_thread' 340 | - 'for_each_prop_codec_conf' 341 | - 'for_each_prop_dai_codec' 342 | - 'for_each_prop_dai_cpu' 343 | - 'for_each_prop_dlc_codecs' 344 | - 'for_each_prop_dlc_cpus' 345 | - 'for_each_prop_dlc_platforms' 346 | - 'for_each_property_of_node' 347 | - 'for_each_reg' 348 | - 'for_each_reg_filtered' 349 | - 'for_each_registered_fb' 350 | - 'for_each_requested_gpio' 351 | - 'for_each_requested_gpio_in_range' 352 | - 'for_each_reserved_mem_range' 353 | - 'for_each_reserved_mem_region' 354 | - 'for_each_rtd_codec_dais' 355 | - 'for_each_rtd_components' 356 | - 'for_each_rtd_cpu_dais' 357 | - 'for_each_rtd_dais' 358 | - 'for_each_script' 359 | - 'for_each_sec' 360 | - 'for_each_set_bit' 361 | - 'for_each_set_bit_from' 362 | - 'for_each_set_bitrange' 363 | - 'for_each_set_bitrange_from' 364 | - 'for_each_set_clump8' 365 | - 'for_each_sg' 366 | - 'for_each_sg_dma_page' 367 | - 'for_each_sg_page' 368 | - 'for_each_sgtable_dma_page' 369 | - 'for_each_sgtable_dma_sg' 370 | - 'for_each_sgtable_page' 371 | - 'for_each_sgtable_sg' 372 | - 'for_each_shell_test' 373 | - 'for_each_sibling_event' 374 | - 'for_each_subelement' 375 | - 'for_each_subelement_extid' 376 | - 'for_each_subelement_id' 377 | - 'for_each_sublist' 378 | - 'for_each_subsystem' 379 | - 'for_each_supported_activate_fn' 380 | - 'for_each_supported_inject_fn' 381 | - 'for_each_test' 382 | - 'for_each_thread' 383 | - 'for_each_token' 384 | - 'for_each_unicast_dest_pgid' 385 | - 'for_each_vsi' 386 | - 'for_each_wakeup_source' 387 | - 'for_each_zone' 388 | - 'for_each_zone_zonelist' 389 | - 'for_each_zone_zonelist_nodemask' 390 | - 'func_for_each_insn' 391 | - 'fwnode_for_each_available_child_node' 392 | - 'fwnode_for_each_child_node' 393 | - 'fwnode_graph_for_each_endpoint' 394 | - 'gadget_for_each_ep' 395 | - 'genradix_for_each' 396 | - 'genradix_for_each_from' 397 | - 'hash_for_each' 398 | - 'hash_for_each_possible' 399 | - 'hash_for_each_possible_rcu' 400 | - 'hash_for_each_possible_rcu_notrace' 401 | - 'hash_for_each_possible_safe' 402 | - 'hash_for_each_rcu' 403 | - 'hash_for_each_safe' 404 | - 'hashmap__for_each_entry' 405 | - 'hashmap__for_each_entry_safe' 406 | - 'hashmap__for_each_key_entry' 407 | - 'hashmap__for_each_key_entry_safe' 408 | - 'hctx_for_each_ctx' 409 | - 'hists__for_each_format' 410 | - 'hists__for_each_sort_list' 411 | - 'hlist_bl_for_each_entry' 412 | - 'hlist_bl_for_each_entry_rcu' 413 | - 'hlist_bl_for_each_entry_safe' 414 | - 'hlist_for_each' 415 | - 'hlist_for_each_entry' 416 | - 'hlist_for_each_entry_continue' 417 | - 'hlist_for_each_entry_continue_rcu' 418 | - 'hlist_for_each_entry_continue_rcu_bh' 419 | - 'hlist_for_each_entry_from' 420 | - 'hlist_for_each_entry_from_rcu' 421 | - 'hlist_for_each_entry_rcu' 422 | - 'hlist_for_each_entry_rcu_bh' 423 | - 'hlist_for_each_entry_rcu_notrace' 424 | - 'hlist_for_each_entry_safe' 425 | - 'hlist_for_each_entry_srcu' 426 | - 'hlist_for_each_safe' 427 | - 'hlist_nulls_for_each_entry' 428 | - 'hlist_nulls_for_each_entry_from' 429 | - 'hlist_nulls_for_each_entry_rcu' 430 | - 'hlist_nulls_for_each_entry_safe' 431 | - 'i3c_bus_for_each_i2cdev' 432 | - 'i3c_bus_for_each_i3cdev' 433 | - 'idr_for_each_entry' 434 | - 'idr_for_each_entry_continue' 435 | - 'idr_for_each_entry_continue_ul' 436 | - 'idr_for_each_entry_ul' 437 | - 'in_dev_for_each_ifa_rcu' 438 | - 'in_dev_for_each_ifa_rtnl' 439 | - 'inet_bind_bucket_for_each' 440 | - 'inet_lhash2_for_each_icsk' 441 | - 'inet_lhash2_for_each_icsk_continue' 442 | - 'inet_lhash2_for_each_icsk_rcu' 443 | - 'intlist__for_each_entry' 444 | - 'intlist__for_each_entry_safe' 445 | - 'kcore_copy__for_each_phdr' 446 | - 'key_for_each' 447 | - 'key_for_each_safe' 448 | - 'klp_for_each_func' 449 | - 'klp_for_each_func_safe' 450 | - 'klp_for_each_func_static' 451 | - 'klp_for_each_object' 452 | - 'klp_for_each_object_safe' 453 | - 'klp_for_each_object_static' 454 | - 'kunit_suite_for_each_test_case' 455 | - 'kvm_for_each_memslot' 456 | - 'kvm_for_each_memslot_in_gfn_range' 457 | - 'kvm_for_each_vcpu' 458 | - 'libbpf_nla_for_each_attr' 459 | - 'list_for_each' 460 | - 'list_for_each_codec' 461 | - 'list_for_each_codec_safe' 462 | - 'list_for_each_continue' 463 | - 'list_for_each_entry' 464 | - 'list_for_each_entry_continue' 465 | - 'list_for_each_entry_continue_rcu' 466 | - 'list_for_each_entry_continue_reverse' 467 | - 'list_for_each_entry_from' 468 | - 'list_for_each_entry_from_rcu' 469 | - 'list_for_each_entry_from_reverse' 470 | - 'list_for_each_entry_lockless' 471 | - 'list_for_each_entry_rcu' 472 | - 'list_for_each_entry_reverse' 473 | - 'list_for_each_entry_safe' 474 | - 'list_for_each_entry_safe_continue' 475 | - 'list_for_each_entry_safe_from' 476 | - 'list_for_each_entry_safe_reverse' 477 | - 'list_for_each_entry_srcu' 478 | - 'list_for_each_from' 479 | - 'list_for_each_prev' 480 | - 'list_for_each_prev_safe' 481 | - 'list_for_each_safe' 482 | - 'llist_for_each' 483 | - 'llist_for_each_entry' 484 | - 'llist_for_each_entry_safe' 485 | - 'llist_for_each_safe' 486 | - 'map__for_each_symbol' 487 | - 'map__for_each_symbol_by_name' 488 | - 'map_for_each_event' 489 | - 'map_for_each_metric' 490 | - 'maps__for_each_entry' 491 | - 'maps__for_each_entry_safe' 492 | - 'mci_for_each_dimm' 493 | - 'media_device_for_each_entity' 494 | - 'media_device_for_each_intf' 495 | - 'media_device_for_each_link' 496 | - 'media_device_for_each_pad' 497 | - 'msi_for_each_desc' 498 | - 'nanddev_io_for_each_page' 499 | - 'netdev_for_each_lower_dev' 500 | - 'netdev_for_each_lower_private' 501 | - 'netdev_for_each_lower_private_rcu' 502 | - 'netdev_for_each_mc_addr' 503 | - 'netdev_for_each_uc_addr' 504 | - 'netdev_for_each_upper_dev_rcu' 505 | - 'netdev_hw_addr_list_for_each' 506 | - 'nft_rule_for_each_expr' 507 | - 'nla_for_each_attr' 508 | - 'nla_for_each_nested' 509 | - 'nlmsg_for_each_attr' 510 | - 'nlmsg_for_each_msg' 511 | - 'nr_neigh_for_each' 512 | - 'nr_neigh_for_each_safe' 513 | - 'nr_node_for_each' 514 | - 'nr_node_for_each_safe' 515 | - 'of_for_each_phandle' 516 | - 'of_property_for_each_string' 517 | - 'of_property_for_each_u32' 518 | - 'pci_bus_for_each_resource' 519 | - 'pci_doe_for_each_off' 520 | - 'pcl_for_each_chunk' 521 | - 'pcl_for_each_segment' 522 | - 'pcm_for_each_format' 523 | - 'perf_config_items__for_each_entry' 524 | - 'perf_config_sections__for_each_entry' 525 | - 'perf_config_set__for_each_entry' 526 | - 'perf_cpu_map__for_each_cpu' 527 | - 'perf_evlist__for_each_entry' 528 | - 'perf_evlist__for_each_entry_reverse' 529 | - 'perf_evlist__for_each_entry_safe' 530 | - 'perf_evlist__for_each_evsel' 531 | - 'perf_evlist__for_each_mmap' 532 | - 'perf_hpp_list__for_each_format' 533 | - 'perf_hpp_list__for_each_format_safe' 534 | - 'perf_hpp_list__for_each_sort_list' 535 | - 'perf_hpp_list__for_each_sort_list_safe' 536 | - 'perf_pmu__for_each_hybrid_pmu' 537 | - 'ping_portaddr_for_each_entry' 538 | - 'ping_portaddr_for_each_entry_rcu' 539 | - 'plist_for_each' 540 | - 'plist_for_each_continue' 541 | - 'plist_for_each_entry' 542 | - 'plist_for_each_entry_continue' 543 | - 'plist_for_each_entry_safe' 544 | - 'plist_for_each_safe' 545 | - 'pnp_for_each_card' 546 | - 'pnp_for_each_dev' 547 | - 'protocol_for_each_card' 548 | - 'protocol_for_each_dev' 549 | - 'queue_for_each_hw_ctx' 550 | - 'radix_tree_for_each_slot' 551 | - 'radix_tree_for_each_tagged' 552 | - 'rb_for_each' 553 | - 'rbtree_postorder_for_each_entry_safe' 554 | - 'rdma_for_each_block' 555 | - 'rdma_for_each_port' 556 | - 'rdma_umem_for_each_dma_block' 557 | - 'resort_rb__for_each_entry' 558 | - 'resource_list_for_each_entry' 559 | - 'resource_list_for_each_entry_safe' 560 | - 'rhl_for_each_entry_rcu' 561 | - 'rhl_for_each_rcu' 562 | - 'rht_for_each' 563 | - 'rht_for_each_entry' 564 | - 'rht_for_each_entry_from' 565 | - 'rht_for_each_entry_rcu' 566 | - 'rht_for_each_entry_rcu_from' 567 | - 'rht_for_each_entry_safe' 568 | - 'rht_for_each_from' 569 | - 'rht_for_each_rcu' 570 | - 'rht_for_each_rcu_from' 571 | - 'rq_for_each_bvec' 572 | - 'rq_for_each_segment' 573 | - 'rq_list_for_each' 574 | - 'rq_list_for_each_safe' 575 | - 'scsi_for_each_prot_sg' 576 | - 'scsi_for_each_sg' 577 | - 'sctp_for_each_hentry' 578 | - 'sctp_skb_for_each' 579 | - 'sec_for_each_insn' 580 | - 'sec_for_each_insn_continue' 581 | - 'sec_for_each_insn_from' 582 | - 'shdma_for_each_chan' 583 | - 'shost_for_each_device' 584 | - 'sk_for_each' 585 | - 'sk_for_each_bound' 586 | - 'sk_for_each_entry_offset_rcu' 587 | - 'sk_for_each_from' 588 | - 'sk_for_each_rcu' 589 | - 'sk_for_each_safe' 590 | - 'sk_nulls_for_each' 591 | - 'sk_nulls_for_each_from' 592 | - 'sk_nulls_for_each_rcu' 593 | - 'snd_array_for_each' 594 | - 'snd_pcm_group_for_each_entry' 595 | - 'snd_soc_dapm_widget_for_each_path' 596 | - 'snd_soc_dapm_widget_for_each_path_safe' 597 | - 'snd_soc_dapm_widget_for_each_sink_path' 598 | - 'snd_soc_dapm_widget_for_each_source_path' 599 | - 'strlist__for_each_entry' 600 | - 'strlist__for_each_entry_safe' 601 | - 'sym_for_each_insn' 602 | - 'sym_for_each_insn_continue_reverse' 603 | - 'symbols__for_each_entry' 604 | - 'tb_property_for_each' 605 | - 'tcf_act_for_each_action' 606 | - 'tcf_exts_for_each_action' 607 | - 'udp_portaddr_for_each_entry' 608 | - 'udp_portaddr_for_each_entry_rcu' 609 | - 'usb_hub_for_each_child' 610 | - 'v4l2_device_for_each_subdev' 611 | - 'v4l2_m2m_for_each_dst_buf' 612 | - 'v4l2_m2m_for_each_dst_buf_safe' 613 | - 'v4l2_m2m_for_each_src_buf' 614 | - 'v4l2_m2m_for_each_src_buf_safe' 615 | - 'virtio_device_for_each_vq' 616 | - 'while_for_each_ftrace_op' 617 | - 'xa_for_each' 618 | - 'xa_for_each_marked' 619 | - 'xa_for_each_range' 620 | - 'xa_for_each_start' 621 | - 'xas_for_each' 622 | - 'xas_for_each_conflict' 623 | - 'xas_for_each_marked' 624 | - 'xbc_array_for_each_value' 625 | - 'xbc_for_each_key_value' 626 | - 'xbc_node_for_each_array_value' 627 | - 'xbc_node_for_each_child' 628 | - 'xbc_node_for_each_key_value' 629 | - 'xbc_node_for_each_subkey' 630 | - 'zorro_for_each_dev' 631 | 632 | IncludeBlocks: Preserve 633 | IncludeCategories: 634 | - Regex: '.*' 635 | Priority: 1 636 | IncludeIsMainRegex: '(Test)?$' 637 | IndentCaseLabels: false 638 | IndentGotoLabels: false 639 | IndentPPDirectives: None 640 | IndentWidth: 8 641 | IndentWrappedFunctionNames: false 642 | JavaScriptQuotes: Leave 643 | JavaScriptWrapImports: true 644 | KeepEmptyLinesAtTheStartOfBlocks: false 645 | MacroBlockBegin: '' 646 | MacroBlockEnd: '' 647 | MaxEmptyLinesToKeep: 1 648 | NamespaceIndentation: None 649 | ObjCBinPackProtocolList: Auto 650 | ObjCBlockIndentWidth: 8 651 | ObjCSpaceAfterProperty: true 652 | ObjCSpaceBeforeProtocolList: true 653 | 654 | # Taken from git's rules 655 | PenaltyBreakAssignment: 10 656 | PenaltyBreakBeforeFirstCallParameter: 30 657 | PenaltyBreakComment: 10 658 | PenaltyBreakFirstLessLess: 0 659 | PenaltyBreakString: 10 660 | PenaltyExcessCharacter: 100 661 | PenaltyReturnTypeOnItsOwnLine: 60 662 | 663 | PointerAlignment: Right 664 | ReflowComments: false 665 | SortIncludes: false 666 | SortUsingDeclarations: false 667 | SpaceAfterCStyleCast: false 668 | SpaceAfterTemplateKeyword: true 669 | SpaceBeforeAssignmentOperators: true 670 | SpaceBeforeCtorInitializerColon: true 671 | SpaceBeforeInheritanceColon: true 672 | SpaceBeforeParens: ControlStatementsExceptForEachMacros 673 | SpaceBeforeRangeBasedForLoopColon: true 674 | SpaceInEmptyParentheses: false 675 | SpacesBeforeTrailingComments: 1 676 | SpacesInAngles: false 677 | SpacesInContainerLiterals: false 678 | SpacesInCStyleCastParentheses: false 679 | SpacesInParentheses: false 680 | SpacesInSquareBrackets: false 681 | Standard: Cpp03 682 | TabWidth: 8 683 | UseTab: Always 684 | ... 685 | -------------------------------------------------------------------------------- /hw_breakpoint.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "ext_hw_breakpoint.h" 10 | 11 | /*func extern*/ 12 | extern int hw_bp_manage_init(void); 13 | extern int hw_proc_init(void); 14 | 15 | enum hw_breakpoint_ops { 16 | HW_BREAKPOINT_INSTALL, 17 | HW_BREAKPOINT_UNINSTALL, 18 | HW_BREAKPOINT_RESTORE 19 | }; 20 | 21 | /* Breakpoint currently in use for each BRP. */ 22 | static DEFINE_PER_CPU(struct hw_bp_info *, bp_on_reg[ARM_MAX_BRP]); 23 | 24 | /* Watchpoint currently in use for each WRP. */ 25 | static DEFINE_PER_CPU(struct hw_bp_info *, wp_on_reg[ARM_MAX_WRP]); 26 | 27 | /* Currently stepping a per-CPU kernel breakpoint. */ 28 | static DEFINE_PER_CPU(int, stepping_kernel_bp); 29 | 30 | /* Number of BRP/WRP registers on this CPU. */ 31 | static int core_num_brps; 32 | static int core_num_wrps; 33 | 34 | /*kernel vars*/ 35 | hw_kernel_api g_kernel_api; 36 | 37 | /*get bp num*/ 38 | int hw_get_bp_num(int type) 39 | { 40 | switch (type) { 41 | case TYPE_INST: 42 | return hw_get_num_brps(); 43 | case TYPE_DATA: 44 | return hw_get_num_wrps(); 45 | default: 46 | pr_info("unknown slot type: %d\n", type); 47 | return 0; 48 | } 49 | } 50 | 51 | #define READ_WB_REG_CASE(OFF, N, REG, VAL) \ 52 | case ((OFF) + (N)): \ 53 | AARCH64_DBG_READ(N, REG, VAL); \ 54 | break 55 | 56 | #define WRITE_WB_REG_CASE(OFF, N, REG, VAL) \ 57 | case ((OFF) + (N)): \ 58 | AARCH64_DBG_WRITE(N, REG, VAL); \ 59 | break 60 | 61 | #define GEN_READ_WB_REG_CASES(OFF, REG, VAL) \ 62 | READ_WB_REG_CASE(OFF, 0, REG, VAL); \ 63 | READ_WB_REG_CASE(OFF, 1, REG, VAL); \ 64 | READ_WB_REG_CASE(OFF, 2, REG, VAL); \ 65 | READ_WB_REG_CASE(OFF, 3, REG, VAL); \ 66 | READ_WB_REG_CASE(OFF, 4, REG, VAL); \ 67 | READ_WB_REG_CASE(OFF, 5, REG, VAL); \ 68 | READ_WB_REG_CASE(OFF, 6, REG, VAL); \ 69 | READ_WB_REG_CASE(OFF, 7, REG, VAL); \ 70 | READ_WB_REG_CASE(OFF, 8, REG, VAL); \ 71 | READ_WB_REG_CASE(OFF, 9, REG, VAL); \ 72 | READ_WB_REG_CASE(OFF, 10, REG, VAL); \ 73 | READ_WB_REG_CASE(OFF, 11, REG, VAL); \ 74 | READ_WB_REG_CASE(OFF, 12, REG, VAL); \ 75 | READ_WB_REG_CASE(OFF, 13, REG, VAL); \ 76 | READ_WB_REG_CASE(OFF, 14, REG, VAL); \ 77 | READ_WB_REG_CASE(OFF, 15, REG, VAL) 78 | 79 | #define GEN_WRITE_WB_REG_CASES(OFF, REG, VAL) \ 80 | WRITE_WB_REG_CASE(OFF, 0, REG, VAL); \ 81 | WRITE_WB_REG_CASE(OFF, 1, REG, VAL); \ 82 | WRITE_WB_REG_CASE(OFF, 2, REG, VAL); \ 83 | WRITE_WB_REG_CASE(OFF, 3, REG, VAL); \ 84 | WRITE_WB_REG_CASE(OFF, 4, REG, VAL); \ 85 | WRITE_WB_REG_CASE(OFF, 5, REG, VAL); \ 86 | WRITE_WB_REG_CASE(OFF, 6, REG, VAL); \ 87 | WRITE_WB_REG_CASE(OFF, 7, REG, VAL); \ 88 | WRITE_WB_REG_CASE(OFF, 8, REG, VAL); \ 89 | WRITE_WB_REG_CASE(OFF, 9, REG, VAL); \ 90 | WRITE_WB_REG_CASE(OFF, 10, REG, VAL); \ 91 | WRITE_WB_REG_CASE(OFF, 11, REG, VAL); \ 92 | WRITE_WB_REG_CASE(OFF, 12, REG, VAL); \ 93 | WRITE_WB_REG_CASE(OFF, 13, REG, VAL); \ 94 | WRITE_WB_REG_CASE(OFF, 14, REG, VAL); \ 95 | WRITE_WB_REG_CASE(OFF, 15, REG, VAL) 96 | 97 | /*read bp reg*/ 98 | static u64 hw_read_bp_reg(int reg, int n) 99 | { 100 | u64 val = 0; 101 | 102 | switch (reg + n) { 103 | GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_BVR, 104 | AARCH64_DBG_REG_NAME_BVR, val); 105 | GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_BCR, 106 | AARCH64_DBG_REG_NAME_BCR, val); 107 | GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_WVR, 108 | AARCH64_DBG_REG_NAME_WVR, val); 109 | GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_WCR, 110 | AARCH64_DBG_REG_NAME_WCR, val); 111 | default: 112 | pr_info("attempt to read from unknown breakpoint register %d\n", 113 | n); 114 | } 115 | 116 | return val; 117 | } 118 | NOKPROBE_SYMBOL(hw_read_bp_reg); 119 | 120 | /*write bp reg*/ 121 | static void hw_write_bp_reg(int reg, int n, u64 val) 122 | { 123 | switch (reg + n) { 124 | GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_BVR, 125 | AARCH64_DBG_REG_NAME_BVR, val); 126 | GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_BCR, 127 | AARCH64_DBG_REG_NAME_BCR, val); 128 | GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_WVR, 129 | AARCH64_DBG_REG_NAME_WVR, val); 130 | GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_WCR, 131 | AARCH64_DBG_REG_NAME_WCR, val); 132 | default: 133 | pr_info("attempt to write to unknown breakpoint register %d\n", 134 | n); 135 | } 136 | /*Clear the pipeline to ensure that all previous instructions have been completed before the new instructions are executed*/ 137 | isb(); 138 | } 139 | NOKPROBE_SYMBOL(hw_write_bp_reg); 140 | 141 | /*get elx level*/ 142 | static enum dbg_active_el hw_get_debug_exception_level(int privilege) 143 | { 144 | switch (privilege) { 145 | case AARCH64_BREAKPOINT_EL0: 146 | return DBG_ACTIVE_EL0; 147 | case AARCH64_BREAKPOINT_EL1: 148 | return DBG_ACTIVE_EL1; 149 | default: 150 | pr_info("invalid breakpoint privilege level %d\n", privilege); 151 | return -EINVAL; 152 | } 153 | } 154 | NOKPROBE_SYMBOL(hw_get_debug_exception_level); 155 | 156 | /** 157 | * hw_bp_slot_setup - Insert/remove bp in global variables 158 | * 159 | * @slots: pointer to the global variables 160 | * @max_slots: max bp num 161 | * @bp: bp info 162 | * @ops: type of bp 163 | * 164 | * Return: 165 | * success: return the number of bp 166 | * -ENOSPC no space 167 | * -EINVAL cmd ops 168 | */ 169 | static int hw_bp_slot_setup(struct hw_bp_info **slots, int max_slots, 170 | struct hw_bp_info *bp, enum hw_breakpoint_ops ops) 171 | { 172 | int i; 173 | struct hw_bp_info **slot; 174 | 175 | for (i = 0; i < max_slots; ++i) { 176 | slot = &slots[i]; 177 | switch (ops) { 178 | case HW_BREAKPOINT_INSTALL: 179 | if (!*slot) { 180 | *slot = bp; 181 | return i; 182 | } 183 | break; 184 | case HW_BREAKPOINT_UNINSTALL: 185 | if (*slot == bp) { 186 | *slot = NULL; 187 | return i; 188 | } 189 | break; 190 | case HW_BREAKPOINT_RESTORE: 191 | if (*slot == bp) 192 | return i; 193 | break; 194 | default: 195 | pr_info("Unhandled hw breakpoint ops %d\n", ops); 196 | return -EINVAL; 197 | } 198 | } 199 | return -ENOSPC; 200 | } 201 | 202 | /*bp control install/uninstall*/ 203 | static int hw_bp_control(struct hw_bp_info *bp, enum hw_breakpoint_ops ops) 204 | { 205 | hw_bp_vc *info = hw_get_vc(bp); 206 | struct hw_bp_info **slots; 207 | int i, max_slots, ctrl_reg, val_reg; 208 | enum dbg_active_el dbg_el = 209 | hw_get_debug_exception_level(info->ctrl.privilege); 210 | u32 ctrl; 211 | 212 | // pr_info("the real CPU = %d\n", smp_processor_id()); 213 | 214 | if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { 215 | /* Breakpoint */ 216 | ctrl_reg = AARCH64_DBG_REG_BCR; 217 | val_reg = AARCH64_DBG_REG_BVR; 218 | slots = this_cpu_ptr(bp_on_reg); 219 | max_slots = core_num_brps; 220 | } else { 221 | /* Watchpoint */ 222 | ctrl_reg = AARCH64_DBG_REG_WCR; 223 | val_reg = AARCH64_DBG_REG_WVR; 224 | slots = this_cpu_ptr(wp_on_reg); 225 | max_slots = core_num_wrps; 226 | } 227 | 228 | i = hw_bp_slot_setup(slots, max_slots, bp, ops); 229 | 230 | if (WARN_ONCE(i < 0, "Can't find any breakpoint slot")) 231 | return i; 232 | 233 | switch (ops) { 234 | case HW_BREAKPOINT_INSTALL: 235 | /*Ensure debug monitors are enabled at the correct exception level.*/ 236 | HW_SYMS_FUNC(enable_debug_monitors)(dbg_el); 237 | fallthrough; 238 | /* Fall through */ 239 | case HW_BREAKPOINT_RESTORE: 240 | /* Setup the address register. */ 241 | hw_write_bp_reg(val_reg, i, info->address); 242 | 243 | /* Setup the control register. */ 244 | ctrl = hw_encode_ctrl_reg(info->ctrl); 245 | // pr_info("CTRL REG = %x\n", ctrl); 246 | hw_write_bp_reg(ctrl_reg, i, ctrl); 247 | break; 248 | case HW_BREAKPOINT_UNINSTALL: 249 | /* Reset the control register. */ 250 | hw_write_bp_reg(ctrl_reg, i, 0); 251 | 252 | /*Release the debug monitors for the correct exception level.*/ 253 | HW_SYMS_FUNC(disable_debug_monitors)(dbg_el); 254 | break; 255 | } 256 | 257 | return 0; 258 | } 259 | 260 | /* 261 | * Install a breakpoint. 262 | */ 263 | int hw_bp_install(struct hw_bp_info *bp) 264 | { 265 | return hw_bp_control(bp, HW_BREAKPOINT_INSTALL); 266 | } 267 | 268 | int hw_bp_uninstall(struct hw_bp_info *bp) 269 | { 270 | return hw_bp_control(bp, HW_BREAKPOINT_UNINSTALL); 271 | } 272 | 273 | /*get len from LBN bit*/ 274 | static int hw_get_hbp_Len(u8 hbp_len) 275 | { 276 | int len_in_bytes = 0; 277 | 278 | switch (hbp_len) { 279 | case ARM_BREAKPOINT_LEN_1: 280 | len_in_bytes = 1; 281 | break; 282 | case ARM_BREAKPOINT_LEN_2: 283 | len_in_bytes = 2; 284 | break; 285 | case ARM_BREAKPOINT_LEN_3: 286 | len_in_bytes = 3; 287 | break; 288 | case ARM_BREAKPOINT_LEN_4: 289 | len_in_bytes = 4; 290 | break; 291 | case ARM_BREAKPOINT_LEN_5: 292 | len_in_bytes = 5; 293 | break; 294 | case ARM_BREAKPOINT_LEN_6: 295 | len_in_bytes = 6; 296 | break; 297 | case ARM_BREAKPOINT_LEN_7: 298 | len_in_bytes = 7; 299 | break; 300 | case ARM_BREAKPOINT_LEN_8: 301 | default: 302 | len_in_bytes = 8; 303 | break; 304 | } 305 | 306 | return len_in_bytes; 307 | } 308 | 309 | /* 310 | * Check whether bp virtual address is in kernel space. 311 | */ 312 | int hw_arch_check_bp_in_kspace(hw_bp_vc *hw) 313 | { 314 | unsigned int len; 315 | unsigned long va; 316 | 317 | va = hw->address; 318 | len = hw_get_hbp_Len(hw->ctrl.len); 319 | 320 | /*get addr & len from mask*/ 321 | if (hw->ctrl.mask) { 322 | len = 1 << hw->ctrl.mask; 323 | } 324 | 325 | return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); 326 | } 327 | 328 | /* 329 | * bp info to ctrl reg 330 | */ 331 | static int hw_arch_build_bp_info(struct hw_bp_info *bp, const hw_bp_attr *attr, 332 | hw_bp_vc *hw) 333 | { 334 | /* Type */ 335 | switch (attr->type) { 336 | case HW_BREAKPOINT_X: 337 | hw->ctrl.type = ARM_BREAKPOINT_EXECUTE; 338 | break; 339 | case HW_BREAKPOINT_R: 340 | hw->ctrl.type = ARM_BREAKPOINT_LOAD; 341 | break; 342 | case HW_BREAKPOINT_W: 343 | hw->ctrl.type = ARM_BREAKPOINT_STORE; 344 | break; 345 | case HW_BREAKPOINT_RW: 346 | hw->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE; 347 | break; 348 | default: 349 | return -EINVAL; 350 | } 351 | 352 | /* Len */ 353 | switch (attr->real_len) { 354 | case HW_BREAKPOINT_LEN_1: 355 | hw->ctrl.len = ARM_BREAKPOINT_LEN_1; 356 | break; 357 | case HW_BREAKPOINT_LEN_2: 358 | hw->ctrl.len = ARM_BREAKPOINT_LEN_2; 359 | break; 360 | case HW_BREAKPOINT_LEN_3: 361 | hw->ctrl.len = ARM_BREAKPOINT_LEN_3; 362 | break; 363 | case HW_BREAKPOINT_LEN_4: 364 | hw->ctrl.len = ARM_BREAKPOINT_LEN_4; 365 | break; 366 | case HW_BREAKPOINT_LEN_5: 367 | hw->ctrl.len = ARM_BREAKPOINT_LEN_5; 368 | break; 369 | case HW_BREAKPOINT_LEN_6: 370 | hw->ctrl.len = ARM_BREAKPOINT_LEN_6; 371 | break; 372 | case HW_BREAKPOINT_LEN_7: 373 | hw->ctrl.len = ARM_BREAKPOINT_LEN_7; 374 | break; 375 | case HW_BREAKPOINT_LEN_8: 376 | hw->ctrl.len = ARM_BREAKPOINT_LEN_8; 377 | break; 378 | default: 379 | return -EINVAL; 380 | } 381 | 382 | /* only permit breakpoints of length 4 */ 383 | if (hw->ctrl.type == ARM_BREAKPOINT_EXECUTE) { 384 | hw->ctrl.len = ARM_BREAKPOINT_LEN_4; 385 | } 386 | 387 | /* wp addr mask */ 388 | hw->ctrl.mask = attr->mask; 389 | /* Address */ 390 | hw->address = attr->start_addr; 391 | 392 | /* 393 | * Privilege 394 | * Note that we disallow combined EL0/EL1 breakpoints because 395 | * that would complicate the stepping code. 396 | */ 397 | if (hw_arch_check_bp_in_kspace(hw)) 398 | hw->ctrl.privilege = AARCH64_BREAKPOINT_EL1; 399 | else 400 | hw->ctrl.privilege = AARCH64_BREAKPOINT_EL0; 401 | 402 | /* Enabled */ 403 | hw->ctrl.enabled = !attr->disabled; 404 | 405 | return 0; 406 | } 407 | 408 | /* parse bp info */ 409 | int hw_bp_arch_parse(struct hw_bp_info *bp, const hw_bp_attr *attr, 410 | hw_bp_vc *hw) 411 | { 412 | int ret; 413 | 414 | /* Build the arch_hw_breakpoint. */ 415 | ret = hw_arch_build_bp_info(bp, attr, hw); 416 | if (ret) 417 | return ret; 418 | 419 | pr_info("ctrl.len=%x,mask=%d,enabled=%d,address=%llx\n", hw->ctrl.len, 420 | hw->ctrl.mask, hw->ctrl.enabled, hw->address); 421 | 422 | return 0; 423 | } 424 | 425 | /* enable/disable a bp */ 426 | static void hw_toggle_bp_registers(int reg, enum dbg_active_el el, int enable) 427 | { 428 | int i, max_slots, privilege; 429 | u32 ctrl; 430 | struct hw_bp_info **slots; 431 | 432 | switch (reg) { 433 | case AARCH64_DBG_REG_BCR: 434 | slots = this_cpu_ptr(bp_on_reg); 435 | max_slots = core_num_brps; 436 | break; 437 | case AARCH64_DBG_REG_WCR: 438 | slots = this_cpu_ptr(wp_on_reg); 439 | max_slots = core_num_wrps; 440 | break; 441 | default: 442 | return; 443 | } 444 | 445 | for (i = 0; i < max_slots; ++i) { 446 | if (!slots[i]) 447 | continue; 448 | 449 | privilege = hw_get_vc(slots[i])->ctrl.privilege; 450 | if (hw_get_debug_exception_level(privilege) != el) 451 | continue; 452 | 453 | ctrl = hw_read_bp_reg(reg, i); 454 | if (enable) 455 | ctrl |= 0x1; 456 | else 457 | ctrl &= ~0x1; 458 | hw_write_bp_reg(reg, i, ctrl); 459 | } 460 | } 461 | NOKPROBE_SYMBOL(hw_toggle_bp_registers); 462 | 463 | /*bp events exception handler*/ 464 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 465 | static int hw_bp_handler(unsigned long unused, unsigned long esr, 466 | struct pt_regs *regs) 467 | #else 468 | static int hw_bp_handler(unsigned long unused, unsigned int esr, 469 | struct pt_regs *regs) 470 | #endif 471 | { 472 | int i, *kernel_step; 473 | u32 ctrl_reg; 474 | u64 addr, val; 475 | struct hw_bp_info *bp, **slots; 476 | hw_bp_ctrl_reg ctrl; 477 | 478 | slots = this_cpu_ptr(bp_on_reg); 479 | addr = instruction_pointer(regs); 480 | 481 | for (i = 0; i < core_num_brps; ++i) { 482 | rcu_read_lock(); 483 | 484 | bp = slots[i]; 485 | 486 | if (bp == NULL) 487 | goto unlock; 488 | 489 | /* Check if the breakpoint value matches. */ 490 | val = hw_read_bp_reg(AARCH64_DBG_REG_BVR, i); 491 | if (val != (addr & ~0x3)) 492 | goto unlock; 493 | 494 | /* Possible match, check the byte address select to confirm. */ 495 | ctrl_reg = hw_read_bp_reg(AARCH64_DBG_REG_BCR, i); 496 | hw_decode_ctrl_reg(ctrl_reg, &ctrl); 497 | if (!((1 << (addr & 0x3)) & ctrl.len)) 498 | goto unlock; 499 | 500 | hw_get_vc(bp)->trigger = addr; 501 | 502 | unlock: 503 | rcu_read_unlock(); 504 | } 505 | 506 | hw_toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL1, 0); 507 | kernel_step = this_cpu_ptr(&stepping_kernel_bp); 508 | 509 | if (*kernel_step != ARM_KERNEL_STEP_NONE) 510 | return 0; 511 | 512 | if (HW_SYMS_FUNC(kernel_active_single_step)()) { 513 | *kernel_step = ARM_KERNEL_STEP_SUSPEND; 514 | } else { 515 | *kernel_step = ARM_KERNEL_STEP_ACTIVE; 516 | HW_SYMS_FUNC(kernel_enable_single_step)(regs); 517 | } 518 | 519 | return 0; 520 | } 521 | NOKPROBE_SYMBOL(hw_bp_handler); 522 | 523 | /*get dist from trigger to wp addr*/ 524 | static u64 hw_get_distance_from_wp(unsigned long addr, u64 val, 525 | hw_bp_ctrl_reg *ctrl) 526 | { 527 | addr = untagged_addr(addr); 528 | val = untagged_addr(val); 529 | return addr - val; 530 | } 531 | 532 | /*wp events exception handler*/ 533 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 534 | static int hw_wp_handler(unsigned long addr, unsigned long esr, 535 | struct pt_regs *regs) 536 | #else 537 | static int hw_wp_handler(unsigned long addr, unsigned int esr, 538 | struct pt_regs *regs) 539 | #endif 540 | { 541 | int i, *kernel_step, access, closest_match = -1; 542 | u64 min_dist = -1, dist; 543 | u32 ctrl_reg; 544 | u64 val; 545 | struct hw_bp_info *wp, **slots; 546 | hw_bp_vc *info = NULL; 547 | hw_bp_ctrl_reg ctrl; 548 | 549 | slots = this_cpu_ptr(wp_on_reg); 550 | 551 | /*find the nearest trigger address*/ 552 | rcu_read_lock(); 553 | for (i = 0; i < core_num_wrps; ++i) { 554 | wp = slots[i]; 555 | if (wp == NULL) 556 | continue; 557 | 558 | /*check type of wp*/ 559 | access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W : 560 | HW_BREAKPOINT_R; 561 | if (!(access & wp->attr.type)) 562 | continue; 563 | 564 | /* Check if the watchpoint value and byte select match. */ 565 | val = hw_read_bp_reg(AARCH64_DBG_REG_WVR, i); 566 | ctrl_reg = hw_read_bp_reg(AARCH64_DBG_REG_WCR, i); 567 | hw_decode_ctrl_reg(ctrl_reg, &ctrl); 568 | dist = hw_get_distance_from_wp(addr, wp->attr.addr, &ctrl); 569 | if (dist < min_dist) { 570 | min_dist = dist; 571 | closest_match = i; 572 | } 573 | /* Is this an exact match? */ 574 | if (dist != 0) 575 | continue; 576 | info = hw_get_vc(wp); 577 | info->trigger = addr; 578 | info->access_type = access; 579 | closest_match = i; 580 | } 581 | if (min_dist > 0 && min_dist != -1) { 582 | /* No exact match found. */ 583 | wp = slots[closest_match]; 584 | info = hw_get_vc(wp); 585 | info->trigger = addr; 586 | info->access_type = access; 587 | } 588 | rcu_read_unlock(); 589 | 590 | /*disable all of wps*/ 591 | hw_toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 0); 592 | kernel_step = this_cpu_ptr(&stepping_kernel_bp); 593 | 594 | if (*kernel_step != ARM_KERNEL_STEP_NONE) 595 | return 0; 596 | 597 | if (HW_SYMS_FUNC(kernel_active_single_step)()) { 598 | *kernel_step = ARM_KERNEL_STEP_SUSPEND; 599 | } else { 600 | *kernel_step = ARM_KERNEL_STEP_ACTIVE; 601 | /*enable ss exception in cur regs*/ 602 | HW_SYMS_FUNC(kernel_enable_single_step)(regs); 603 | } 604 | 605 | return 0; 606 | } 607 | NOKPROBE_SYMBOL(hw_wp_handler); 608 | 609 | /*resume bp states*/ 610 | static int hw_bp_reinstall(struct pt_regs *regs) 611 | { 612 | // struct debug_info *debug_info = ¤t->thread.debug; 613 | int handled_exception = 0, *kernel_step; 614 | 615 | /*get step states*/ 616 | kernel_step = this_cpu_ptr(&stepping_kernel_bp); 617 | 618 | if (*kernel_step != ARM_KERNEL_STEP_NONE) { 619 | hw_toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL1, 1); 620 | hw_toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 1); 621 | 622 | if (*kernel_step != ARM_KERNEL_STEP_SUSPEND) { 623 | HW_SYMS_FUNC(kernel_disable_single_step()); 624 | handled_exception = 1; 625 | } else { 626 | handled_exception = 0; 627 | } 628 | 629 | *kernel_step = ARM_KERNEL_STEP_NONE; 630 | } 631 | 632 | return !handled_exception; 633 | } 634 | NOKPROBE_SYMBOL(hw_bp_reinstall); 635 | 636 | /*bp reset when cold boot*/ 637 | static int hw_bp_reset(unsigned int cpu) 638 | { 639 | int i; 640 | struct hw_bp_info **slots; 641 | /* 642 | * When a CPU goes through cold-boot, it does not have any installed 643 | * slot, so it is safe to share the same function for restoring and 644 | * resetting breakpoints; when a CPU is hotplugged in, it goes 645 | * through the slots, which are all empty, hence it just resets control 646 | * and value for debug registers. 647 | * When this function is triggered on warm-boot through a CPU PM 648 | * notifier some slots might be initialized; if so they are 649 | * reprogrammed according to the debug slots content. 650 | */ 651 | for (slots = this_cpu_ptr(bp_on_reg), i = 0; i < core_num_brps; ++i) { 652 | if (slots[i]) { 653 | hw_bp_control(slots[i], HW_BREAKPOINT_RESTORE); 654 | } else { 655 | hw_write_bp_reg(AARCH64_DBG_REG_BCR, i, 0UL); 656 | hw_write_bp_reg(AARCH64_DBG_REG_BVR, i, 0UL); 657 | } 658 | } 659 | 660 | for (slots = this_cpu_ptr(wp_on_reg), i = 0; i < core_num_wrps; ++i) { 661 | if (slots[i]) { 662 | hw_bp_control(slots[i], HW_BREAKPOINT_RESTORE); 663 | } else { 664 | hw_write_bp_reg(AARCH64_DBG_REG_WCR, i, 0UL); 665 | hw_write_bp_reg(AARCH64_DBG_REG_WVR, i, 0UL); 666 | } 667 | } 668 | 669 | return 0; 670 | } 671 | 672 | static void hw_trigger_handler(struct pt_regs *regs) 673 | { 674 | int i = 0; 675 | struct hw_bp_info *wp, **slots; 676 | hw_bp_callback_data report; 677 | 678 | rcu_read_lock(); 679 | slots = this_cpu_ptr(bp_on_reg); 680 | for (i = 0; i < core_num_brps; ++i) { 681 | wp = slots[i]; 682 | if (wp == NULL) 683 | continue; 684 | if (wp->info.trigger) { 685 | wp->attr.times.exec++; 686 | report.type = HW_BREAKPOINT_X; 687 | report.addr = wp->info.trigger; 688 | report.times = wp->attr.times; 689 | report.type = wp->attr.type; 690 | /*user handler*/ 691 | wp->attr.handler(&report, regs); 692 | 693 | wp->info.trigger = 0; 694 | } 695 | } 696 | slots = this_cpu_ptr(wp_on_reg); 697 | for (i = 0; i < core_num_wrps; ++i) { 698 | wp = slots[i]; 699 | if (wp == NULL) 700 | continue; 701 | if (!wp->info.trigger) { 702 | continue; 703 | } 704 | if (wp->info.trigger >= wp->attr.addr && 705 | wp->info.trigger < wp->attr.addr + wp->attr.len) { 706 | /*The user handler only within the range of addresses that are expected to be detected*/ 707 | if (wp->info.access_type & HW_BREAKPOINT_R) { 708 | wp->attr.times.read++; 709 | } else if (wp->info.access_type & HW_BREAKPOINT_W) { 710 | wp->attr.times.write++; 711 | } 712 | /*user handler*/ 713 | report.type = wp->info.access_type; 714 | report.addr = wp->info.trigger; 715 | report.times = wp->attr.times; 716 | wp->attr.handler(&report, regs); 717 | } 718 | wp->info.trigger = 0; 719 | } 720 | rcu_read_unlock(); 721 | } 722 | 723 | /*ss exception handler, will run user handler*/ 724 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) 725 | static int hw_step_brk_fn(struct pt_regs *regs, unsigned long esr) 726 | #else 727 | static int hw_step_brk_fn(struct pt_regs *regs, unsigned int esr) 728 | #endif 729 | { 730 | int *kernel_step; 731 | 732 | /*step states*/ 733 | kernel_step = this_cpu_ptr(&stepping_kernel_bp); 734 | 735 | if (user_mode(regs) || !(*kernel_step)) 736 | return DBG_HOOK_ERROR; 737 | 738 | hw_trigger_handler(regs); 739 | 740 | if (hw_bp_reinstall(regs)) { 741 | return DBG_HOOK_ERROR; 742 | } 743 | #ifdef CONFIG_KGDB 744 | kgdb_handle_exception(0, SIGTRAP, 0, regs); 745 | #endif 746 | 747 | return DBG_HOOK_HANDLED; 748 | } 749 | NOKPROBE_SYMBOL(hw_step_brk_fn); 750 | 751 | #ifdef CONFIG_CPU_PM 752 | extern void cpu_suspend_set_dbg_restorer(int (*hw_bp_restore)(unsigned int)); 753 | #else 754 | static inline void 755 | cpu_suspend_set_dbg_restorer(int (*hw_bp_restore)(unsigned int)) 756 | { 757 | } 758 | #endif 759 | 760 | static struct step_hook ghw_step_hook = { .fn = hw_step_brk_fn }; 761 | 762 | /*search symbol addr*/ 763 | unsigned long kaddr_lookup_name(const char *fname_raw) 764 | { 765 | int i; 766 | unsigned long kaddr; 767 | char *fname_lookup, *fname; 768 | 769 | fname_lookup = kzalloc(NAME_MAX, GFP_KERNEL); 770 | if (!fname_lookup) 771 | return 0; 772 | 773 | fname = kzalloc(strlen(fname_raw) + 4, GFP_KERNEL); 774 | if (!fname) 775 | return 0; 776 | 777 | /* 778 | * We have to add "+0x0" to the end of our function name 779 | * because that's the format that sprint_symbol() returns 780 | * to us. If we don't do this, then our search can stop 781 | * prematurely and give us the wrong function address! 782 | */ 783 | strcpy(fname, fname_raw); 784 | strcat(fname, "+0x0"); 785 | 786 | /*获取内核代码段基地址*/ 787 | kaddr = (unsigned long)&sprint_symbol; 788 | kaddr &= 0xffffffffff000000; 789 | 790 | /*内核符号不会超过0x100000*16的大小,所以按4字节偏移,挨个找*/ 791 | for (i = 0x0; i < 0x400000; i++) { 792 | /*寻找地址对应的符号名称*/ 793 | sprint_symbol(fname_lookup, kaddr); 794 | /*对比寻找的符号名字*/ 795 | if (strncmp(fname_lookup, fname, strlen(fname)) == 0) { 796 | /*找到了就返回地址*/ 797 | kfree(fname_lookup); 798 | kfree(fname); 799 | return kaddr; 800 | } 801 | /*偏移4字节*/ 802 | kaddr += 0x04; 803 | } 804 | /*没找到地址就返回0*/ 805 | kfree(fname_lookup); 806 | kfree(fname); 807 | return 0; 808 | } 809 | 810 | /*get kallsyms_lookup_name*/ 811 | static int hw_get_kallsyms_lookup_name(void) 812 | { 813 | HW_SYMS_FUNC(kallsyms_lookup_name) = 814 | (void *)kaddr_lookup_name("kallsyms_lookup_name"); 815 | if (!HW_SYMS_FUNC(kallsyms_lookup_name)) { 816 | printk("get kallsyms_lookup_name fail \n"); 817 | return -1; 818 | } 819 | return 0; 820 | } 821 | 822 | /*get vars from kernel*/ 823 | static int hw_get_kernel_api(void) 824 | { 825 | memset(&g_kernel_api, 0, sizeof(g_kernel_api)); 826 | if (hw_get_kallsyms_lookup_name()) { 827 | return -1; 828 | } 829 | HW_SYMS_VAL(debug_fault_info) = 830 | (void *)HW_SYMS_FUNC(kallsyms_lookup_name)("debug_fault_info"); 831 | if (!HW_SYMS_VAL(debug_fault_info)) { 832 | pr_warn("get debug_fault_info fail\n"); 833 | return -1; 834 | } 835 | // pr_warn("debug_fault_info = %llx,name = %s\n", &HW_SYMS_VAL(debug_fault_info)[0], 836 | // HW_SYMS_VAL(debug_fault_info)[0].name); 837 | // pr_warn("debug_fault_info = %llx,name = %s\n", &HW_SYMS_VAL(debug_fault_info)[2], 838 | // HW_SYMS_VAL(debug_fault_info)[2].name); 839 | #ifdef CONFIG_CPU_PM 840 | HW_SYMS_VAL(hw_breakpoint_restore) = (void *)HW_SYMS_FUNC( 841 | kallsyms_lookup_name)("hw_breakpoint_restore"); 842 | if (!HW_SYMS_VAL(hw_breakpoint_restore)) { 843 | pr_warn("get hw_breakpoint_restore fail\n"); 844 | return -1; 845 | } 846 | // pr_warn("hw_breakpoint_restore = %llx,%llx\n", HW_SYMS_VAL(hw_breakpoint_restore), 847 | // *HW_SYMS_VAL(hw_breakpoint_restore)); 848 | #endif 849 | HW_SYMS_FUNC(kernel_active_single_step) = (void *)HW_SYMS_FUNC( 850 | kallsyms_lookup_name)("kernel_active_single_step"); 851 | if (!HW_SYMS_FUNC(kernel_active_single_step)) { 852 | pr_warn("get kernel_active_single_step fail\n"); 853 | return -1; 854 | } 855 | HW_SYMS_FUNC(kernel_disable_single_step) = (void *)HW_SYMS_FUNC( 856 | kallsyms_lookup_name)("kernel_disable_single_step"); 857 | if (!HW_SYMS_FUNC(kernel_disable_single_step)) { 858 | pr_warn("get kernel_disable_single_step fail\n"); 859 | return -1; 860 | } 861 | HW_SYMS_FUNC(kernel_enable_single_step) = (void *)HW_SYMS_FUNC( 862 | kallsyms_lookup_name)("kernel_enable_single_step"); 863 | if (!HW_SYMS_FUNC(kernel_enable_single_step)) { 864 | pr_warn("get kernel_enable_single_step fail\n"); 865 | return -1; 866 | } 867 | HW_SYMS_FUNC(disable_debug_monitors) = (void *)HW_SYMS_FUNC( 868 | kallsyms_lookup_name)("disable_debug_monitors"); 869 | if (!HW_SYMS_FUNC(disable_debug_monitors)) { 870 | pr_warn("get disable_debug_monitors fail\n"); 871 | return -1; 872 | } 873 | HW_SYMS_FUNC(do_bad) = 874 | (void *)HW_SYMS_FUNC(kallsyms_lookup_name)("do_bad"); 875 | if (!HW_SYMS_FUNC(do_bad)) { 876 | pr_warn("get do_bad fail\n"); 877 | return -1; 878 | } 879 | HW_SYMS_FUNC(enable_debug_monitors) = (void *)HW_SYMS_FUNC( 880 | kallsyms_lookup_name)("enable_debug_monitors"); 881 | if (!HW_SYMS_FUNC(enable_debug_monitors)) { 882 | pr_warn("get enable_debug_monitors fail\n"); 883 | return -1; 884 | } 885 | HW_SYMS_FUNC(read_sanitised_ftr_reg) = (void *)HW_SYMS_FUNC( 886 | kallsyms_lookup_name)("read_sanitised_ftr_reg"); 887 | if (!HW_SYMS_FUNC(read_sanitised_ftr_reg)) { 888 | pr_warn("get read_sanitised_ftr_reg fail\n"); 889 | return -1; 890 | } 891 | HW_SYMS_FUNC(show_regs) = 892 | (void *)HW_SYMS_FUNC(kallsyms_lookup_name)("show_regs"); 893 | if (!HW_SYMS_FUNC(show_regs)) { 894 | pr_warn("get show_regs fail\n"); 895 | return -1; 896 | } 897 | HW_SYMS_FUNC(dump_backtrace) = 898 | (void *)HW_SYMS_FUNC(kallsyms_lookup_name)("dump_backtrace"); 899 | if (!HW_SYMS_FUNC(dump_backtrace)) { 900 | pr_warn("get dump_backtrace fail\n"); 901 | return -1; 902 | } 903 | /*5.0以下内核用的是register_step_hook*/ 904 | HW_SYMS_FUNC(register_step_hook) = (void *)HW_SYMS_FUNC( 905 | kallsyms_lookup_name)("register_step_hook"); 906 | if (!HW_SYMS_FUNC(register_step_hook)) { 907 | /*5.0以上内核用的是register_kernel_step_hook*/ 908 | HW_SYMS_FUNC(register_step_hook) = (void *)HW_SYMS_FUNC( 909 | kallsyms_lookup_name)("register_kernel_step_hook"); 910 | if (!HW_SYMS_FUNC(register_step_hook)) { 911 | pr_warn("get register_step_hook fail\n"); 912 | return -1; 913 | } 914 | } 915 | HW_SYMS_FUNC(unregister_step_hook) = (void *)HW_SYMS_FUNC( 916 | kallsyms_lookup_name)("unregister_step_hook"); 917 | if (!HW_SYMS_FUNC(unregister_step_hook)) { 918 | HW_SYMS_FUNC(unregister_step_hook) = (void *)HW_SYMS_FUNC( 919 | kallsyms_lookup_name)("unregister_kernel_step_hook"); 920 | if (!HW_SYMS_FUNC(unregister_step_hook)) { 921 | pr_warn("get unregister_step_hook fail\n"); 922 | return -1; 923 | } 924 | } 925 | 926 | /*以下不影响驱动使用,只影响根据io地址查询虚拟地址功能*/ 927 | HW_SYMS_VAL(vmap_area_lock) = 928 | (void *)HW_SYMS_FUNC(kallsyms_lookup_name)("vmap_area_lock"); 929 | HW_SYMS_VAL(vmap_area_lock) = 930 | (void *)HW_SYMS_FUNC(kallsyms_lookup_name)("vmap_area_list"); 931 | if ((!HW_SYMS_VAL(vmap_area_lock)) || (!HW_SYMS_VAL(vmap_area_lock))) { 932 | pr_warn("can not get virt from iophys\n"); 933 | } 934 | 935 | return 0; 936 | } 937 | 938 | /*hp init*/ 939 | static int __init hw_bp_init(void) 940 | { 941 | if (hw_get_kernel_api()) { 942 | return -1; 943 | } 944 | 945 | core_num_brps = hw_get_num_brps(); 946 | core_num_wrps = hw_get_num_wrps(); 947 | 948 | pr_info("found %d breakpoint and %d watchpoint registers.\n", 949 | core_num_brps, core_num_wrps); 950 | 951 | /* register dbg exception hook */ 952 | /*bp*/ 953 | /*save pre vars*/ 954 | HW_SYMS_VAL(default_fault_info) 955 | [0].fn = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].fn; 956 | HW_SYMS_VAL(default_fault_info) 957 | [0].sig = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].sig; 958 | HW_SYMS_VAL(default_fault_info) 959 | [0].code = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].code; 960 | HW_SYMS_VAL(default_fault_info) 961 | [0].name = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].name; 962 | 963 | /*new*/ 964 | HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].fn = hw_bp_handler; 965 | HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].sig = SIGTRAP; 966 | HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWBP].code = TRAP_HWBKPT; 967 | HW_SYMS_VAL(debug_fault_info) 968 | [DBG_ESR_EVT_HWBP].name = "hw-breakpoint handler"; 969 | /*wp*/ 970 | /*save pre vars*/ 971 | HW_SYMS_VAL(default_fault_info) 972 | [1].fn = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].fn; 973 | HW_SYMS_VAL(default_fault_info) 974 | [1].sig = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].sig; 975 | HW_SYMS_VAL(default_fault_info) 976 | [1].code = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].code; 977 | HW_SYMS_VAL(default_fault_info) 978 | [1].name = HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].name; 979 | /*new*/ 980 | HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].fn = hw_wp_handler; 981 | HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].sig = SIGTRAP; 982 | HW_SYMS_VAL(debug_fault_info)[DBG_ESR_EVT_HWWP].code = TRAP_HWBKPT; 983 | HW_SYMS_VAL(debug_fault_info) 984 | [DBG_ESR_EVT_HWWP].name = "hw-watchpoint handler"; 985 | HW_SYMS_FUNC(register_step_hook)(&ghw_step_hook); 986 | #ifdef CONFIG_CPU_PM 987 | HW_SYMS_VAL(default_hw_breakpoint_restore) = 988 | *HW_SYMS_VAL(hw_breakpoint_restore); 989 | *HW_SYMS_VAL(hw_breakpoint_restore) = (u64)hw_bp_reset; 990 | #endif 991 | hw_bp_manage_init(); 992 | hw_proc_init(); 993 | 994 | pr_info("zwf 11111111111111111111111111111 %s ok\n", __FUNCTION__); 995 | return 0; 996 | } 997 | 998 | static void __exit hw_bp_exit(void) 999 | { 1000 | hw_proc_exit(); 1001 | hw_bp_manage_deinit(); 1002 | #ifdef CONFIG_CPU_PM 1003 | *HW_SYMS_VAL(hw_breakpoint_restore) = 1004 | HW_SYMS_VAL(default_hw_breakpoint_restore); 1005 | #endif 1006 | HW_SYMS_FUNC(unregister_step_hook)(&ghw_step_hook); 1007 | /*wp*/ 1008 | HW_SYMS_VAL(debug_fault_info) 1009 | [DBG_ESR_EVT_HWWP].fn = HW_SYMS_VAL(default_fault_info)[1].fn; 1010 | HW_SYMS_VAL(debug_fault_info) 1011 | [DBG_ESR_EVT_HWWP].sig = HW_SYMS_VAL(default_fault_info)[1].sig; 1012 | HW_SYMS_VAL(debug_fault_info) 1013 | [DBG_ESR_EVT_HWWP].code = HW_SYMS_VAL(default_fault_info)[1].code; 1014 | HW_SYMS_VAL(debug_fault_info) 1015 | [DBG_ESR_EVT_HWWP].name = HW_SYMS_VAL(default_fault_info)[1].name; 1016 | /*bp*/ 1017 | HW_SYMS_VAL(debug_fault_info) 1018 | [DBG_ESR_EVT_HWBP].fn = HW_SYMS_VAL(default_fault_info)[0].fn; 1019 | HW_SYMS_VAL(debug_fault_info) 1020 | [DBG_ESR_EVT_HWBP].sig = HW_SYMS_VAL(default_fault_info)[0].sig; 1021 | HW_SYMS_VAL(debug_fault_info) 1022 | [DBG_ESR_EVT_HWBP].code = HW_SYMS_VAL(default_fault_info)[0].code; 1023 | HW_SYMS_VAL(debug_fault_info) 1024 | [DBG_ESR_EVT_HWBP].name = HW_SYMS_VAL(default_fault_info)[0].name; 1025 | printk(" hw_bp_exit\n"); 1026 | } 1027 | 1028 | module_init(hw_bp_init); 1029 | module_exit(hw_bp_exit); 1030 | 1031 | MODULE_AUTHOR("Vimoon Zheng "); 1032 | MODULE_DESCRIPTION("hardware breakpoint for SKY1 and later"); 1033 | MODULE_LICENSE("GPL v2"); 1034 | MODULE_ALIAS("platform: sky1-bp"); 1035 | --------------------------------------------------------------------------------