ARMV7-A Watchpoint功能验证
一、Watchpoint功能
ARMV7-A 架构的内核自带的硬件watchpoint功能,例如Cortex-A9支持6个硬件breakpoint, 4个硬件watchpoint, gdb的watchpoint大多数情况下就是基于该功能实现。
Watchpoint功能启用之后,一旦处理器对监控区域内的内存地址进行读或写操作,硬件会自动进入Halt或者Exception状态。
二、打开监控模式
使用Watchpoint之前首先要操作DBGDSCR(Debug Status and Control Register)打开监控模式,该寄存器相关的两个Bit如下:
MDBGen和HDBGen分别代表了Watchpoint命中以后进入Monitor模式还是Halt模式。
如果处理器当前处于JTAG调试模式,HDBGen会自动设为1,命中以后程序会暂停,恢复以后会继续运行,不会进入异常;如果要使Watchpoint命中以后进入异常流程,需要把MDBGen设为1,并且不能使用JTAG方式进行启动。
另外如果要处理器进入DebugExeception流程,还需要操作DBGOSLAR(OS Lock Access Register )进行解锁,解锁方法是写入除了Key(0xC5ACCE55)之外的任何值,否则就是加锁。
三、设置watchpoint
设置Watchpoint需要操作DBGWVR和DBGWCR两个寄存器,寄存器功能描述见下图。
其中:DBGWVR用于配置被监控地址(虚拟内存地址,而不是物理内存地址),配置的地址总共30bit, 即地址需要按照四字节对齐。
DBGWCR用于配置监控行为,主要需要配置的bit位有:
bit[0] 表示开关Watchpoint功能(E, bit[0] 0: Watchpoint disabled. 1:Watchpoint enabled.);
bits[2:1] PAC表示特权模式控制;
bits[4:3] LSC表示监控往该内存区域的写入或者读出或者读写操作;
0b00 Reserved. 0b01 Match on any load, Load-Exclusive, or swap. 0b10 Match on any store, Store-Exclusive, or swap. 0b11 Match on all types of access.
bits[12:5] or bits[8:5] BAS Byte address select
Watchpoint监控的虚拟内存地址为字对齐的(32位系统为4字节对齐),每个Watchpoint最大监控长度为4字节(32位系统)。但是如果仅仅想监控1个字节或者2个字节呢?最前面的四个比特0011就是用来做这个事情的。上面的0011代表仅监控addr开始的2个字节。如果只想监控最后一个字节,前4比特可以写为0001。
bits[28:24] MASK Address range mask. 用于配置监控地址的掩码。
If watchpoint address range masking is supported, this field can set a watchpoint on a range of addresses by masking lower order address bits out of the watchpoint comparison. The value of this
field is the number of low order bits of the address that are masked off, except that values of 1 and2 are reserved. Therefore, the meaning of Watchpoint Address range mask values are:
0b00000 No mask.
0b00001 Reserved.
0b00010 Reserved.
0b00011 0x00000007 mask for data address, three bits masked.
0b00100 0x0000000F mask for data address, four bits masked.
0b00101 0x0000001F mask for data address, five bits masked.
... ....
0b11111 0x7FFFFFFF mask for data address, 31 bits masked.
四、异常处理
watchpoint命中以后会进入DataAbort异常,在异常处理流程里面,读取DFSR(Data Fault Status Register), 便可以识别出本次异常的原因,例如是否是Watchpoint引起,是读操作还是写操作等。
读取DFAR( Data Fault Address Register ) 可以得到异常发生的具体地址。
四、参考代码
watchponit.c
1 #include "types.h" 2 3 #define pr_warn printf 4 #define pr_warn_once printf 5 6 #define ARM_OP2_BVR 4 7 #define ARM_OP2_BCR 5 8 #define ARM_OP2_WVR 6 9 #define ARM_OP2_WCR 7 10 11 #define ARM_BASE_BVR 64 12 #define ARM_BASE_BCR 80 13 #define ARM_BASE_WVR 96 14 #define ARM_BASE_WCR 112 15 16 17 #define ARM_DSCR_HDBGEN (1 << 14) /* DSCR halting bits. */ 18 #define ARM_DSCR_MDBGEN (1 << 15) /* DSCR monitor bits. */ 19 20 /* Accessor macros for the debug registers. */ 21 #define ARM_DBG_READ(N, M, OP2, VAL) do {\ 22 asm volatile("mrc p14, 0, %0, " #N "," #M ", " #OP2 : "=r" (VAL));\ 23 } while (0) 24 25 #define ARM_DBG_WRITE(N, M, OP2, VAL) do {\ 26 asm volatile("mcr p14, 0, %0, " #N "," #M ", " #OP2 : : "r" (VAL));\ 27 } while (0) 28 29 30 31 #define isb() __asm__ __volatile__ ("" : : : "memory") 32 33 #define READ_WB_REG_CASE(OP2, M, VAL) \ 34 case ((OP2 << 4) + M): \ 35 ARM_DBG_READ(c0, c ## M, OP2, VAL); \ 36 break 37 38 #define WRITE_WB_REG_CASE(OP2, M, VAL) \ 39 case ((OP2 << 4) + M): \ 40 ARM_DBG_WRITE(c0, c ## M, OP2, VAL); \ 41 break 42 43 #define GEN_READ_WB_REG_CASES(OP2, VAL) \ 44 READ_WB_REG_CASE(OP2, 0, VAL); \ 45 READ_WB_REG_CASE(OP2, 1, VAL); \ 46 READ_WB_REG_CASE(OP2, 2, VAL); \ 47 READ_WB_REG_CASE(OP2, 3, VAL); \ 48 READ_WB_REG_CASE(OP2, 4, VAL); \ 49 READ_WB_REG_CASE(OP2, 5, VAL); \ 50 READ_WB_REG_CASE(OP2, 6, VAL); \ 51 READ_WB_REG_CASE(OP2, 7, VAL); \ 52 READ_WB_REG_CASE(OP2, 8, VAL); \ 53 READ_WB_REG_CASE(OP2, 9, VAL); \ 54 READ_WB_REG_CASE(OP2, 10, VAL); \ 55 READ_WB_REG_CASE(OP2, 11, VAL); \ 56 READ_WB_REG_CASE(OP2, 12, VAL); \ 57 READ_WB_REG_CASE(OP2, 13, VAL); \ 58 READ_WB_REG_CASE(OP2, 14, VAL); \ 59 READ_WB_REG_CASE(OP2, 15, VAL) 60 61 #define GEN_WRITE_WB_REG_CASES(OP2, VAL) \ 62 WRITE_WB_REG_CASE(OP2, 0, VAL); \ 63 WRITE_WB_REG_CASE(OP2, 1, VAL); \ 64 WRITE_WB_REG_CASE(OP2, 2, VAL); \ 65 WRITE_WB_REG_CASE(OP2, 3, VAL); \ 66 WRITE_WB_REG_CASE(OP2, 4, VAL); \ 67 WRITE_WB_REG_CASE(OP2, 5, VAL); \ 68 WRITE_WB_REG_CASE(OP2, 6, VAL); \ 69 WRITE_WB_REG_CASE(OP2, 7, VAL); \ 70 WRITE_WB_REG_CASE(OP2, 8, VAL); \ 71 WRITE_WB_REG_CASE(OP2, 9, VAL); \ 72 WRITE_WB_REG_CASE(OP2, 10, VAL); \ 73 WRITE_WB_REG_CASE(OP2, 11, VAL); \ 74 WRITE_WB_REG_CASE(OP2, 12, VAL); \ 75 WRITE_WB_REG_CASE(OP2, 13, VAL); \ 76 WRITE_WB_REG_CASE(OP2, 14, VAL); \ 77 WRITE_WB_REG_CASE(OP2, 15, VAL) 78 79 static u32 read_wb_reg(int n) 80 { 81 u32 val = 0; 82 83 switch (n) { 84 GEN_READ_WB_REG_CASES(ARM_OP2_BVR, val); 85 GEN_READ_WB_REG_CASES(ARM_OP2_BCR, val); 86 GEN_READ_WB_REG_CASES(ARM_OP2_WVR, val); 87 GEN_READ_WB_REG_CASES(ARM_OP2_WCR, val); 88 default: 89 pr_warn("attempt to read from unknown breakpoint register %d\n", n); 90 } 91 92 return val; 93 } 94 95 static void write_wb_reg(int n, u32 val) 96 { 97 switch (n) { 98 GEN_WRITE_WB_REG_CASES(ARM_OP2_BVR, val); 99 GEN_WRITE_WB_REG_CASES(ARM_OP2_BCR, val); 100 GEN_WRITE_WB_REG_CASES(ARM_OP2_WVR, val); 101 GEN_WRITE_WB_REG_CASES(ARM_OP2_WCR, val); 102 default: 103 pr_warn("attempt to write to unknown breakpoint register %d\n", n); 104 } 105 isb(); 106 } 107 108 109 110 static int enable_monitor_mode(void) 111 { 112 u32 dscr; 113 114 ARM_DBG_WRITE(c1, c0, 4, 0); /*Write OS Lock Access Register n */ 115 ARM_DBG_READ(c0, c1, 0, dscr); 116 117 /* If monitor mode is already enabled, just return. */ 118 if (dscr & ARM_DSCR_MDBGEN) 119 { 120 return 0; 121 } 122 else 123 { 124 ARM_DBG_WRITE(c0, c2, 2, (dscr | ARM_DSCR_MDBGEN)); 125 isb(); 126 } 127 128 /* Check that the write made it through. */ 129 ARM_DBG_READ(c0, c1, 0, dscr); 130 if (!(dscr & ARM_DSCR_MDBGEN)) 131 { 132 pr_warn_once("Failed to enable monitor mode\n"); 133 return -1; 134 } 135 136 } 137 138 /* 139 * i: watchpoint寄存器序号,对于ARM-A9, 可用寄存器为c0~c3, i 取值范围为0~3 140 * addr: 必须是虚拟内存地址,且必须是字对齐的(32位系统为4字节对齐) 141 * we must be running in debug monitor mode. 142 */ 143 144 int arm_install_hw_watchpoint(int i, u32 addr) 145 { 146 u32 ctrl = 1u << 0 | // E = 1 enable 147 3u << 1 | // PAC = 3 0x11 148 3u << 3 | // LSC = 3 load and store 149 15u << 5 | // BAS = 15 150 0u << 13 | // HMC = 0 151 0u << 14 | // SSC = 0 152 0u << 16 | // LBN = 0 153 0u << 20 | // WT = 1 154 18u << 24; // MSK = 0 mask 18 address bit valid addr 0x0~0x3ffff 155 156 u32 check_value; 157 enable_monitor_mode(); 158 159 /* Setup the address register. */ 160 write_wb_reg(ARM_BASE_WVR + i, addr); 161 check_value = read_wb_reg(ARM_BASE_WVR + i); 162 if (check_value != addr) 163 { 164 pr_warn("fail to set WVR[%d] addr:0x%x check_value:0x%x\n", i, addr, check_value); 165 return -1; 166 } 167 168 /* Setup the control register. */ 169 write_wb_reg(ARM_BASE_WCR + i, ctrl); 170 check_value = read_wb_reg(ARM_BASE_WCR + i); 171 if (check_value != ctrl) 172 { 173 pr_warn("fail to set WCR[%d] ctrl:0x%x check_value:0x%x\n", i, ctrl, check_value); 174 return -1; 175 } 176 177 return 0; 178 }
异常处理
DataAbortHandler: /* Data Abort handler */ stmdb sp!,{r0-r3,r12,lr} /* state save from compiled code */ ldr r0, =DataAbortAddr sub r1, lr, #8 str r1, [r0] /* Stores instruction causing data abort */ bl DataAbortInterrupt /*DataAbortInterrupt :call C function here */ b DebugExpHandler subs pc, lr, #8 /* points to the instruction that caused the Data Abort exception */ DebugExpHandler: /* Data Abort handler */ //MRC P15,0,R0,C1,C0,0 /* Disable Icache */ //bic r0,r0,#(1<<12) //MCR P15,0,R0,C1,C0,0 ldr r0, =DataAbortAddr ldr r1, [r0] ldr r0, [r1] ldr r1, =OptAgain str r0, [r1, #0x0] ldr r1, =OptAgainThumb str r0, [r1, #0x4] isb MRC p14, 0, r0, c0, c0, 7 /* GET_WP_DBGWCR0 */ ldr r1, =DbgWCR0Save str r0, [r1] bic r0,r0,#0x1 MCR p14, 0, R0, c0, c0, 7 /* SET_WP_DBGWCR0 */ bl Xil_ICacheInvalidate mrs r0,spsr tst r0, #0x20 /* check the T bit */ bne OptThumb /* thumb mode */ ldmia sp!,{r0-r3,r12,lr} /* state restore from compiled code */ //ldr pc, =OptAgain b OptAgain /*.section .OptArea, "a"*/ OptThumb: ldr r0, =OptAgainThumb add r0,r0,#1 bx r0 OptAgain: b OptAgain isb stmdb sp!,{r0,r1} ldr r1, =DbgWCR0Save ldr r0, [r1] MCR p14, 0, R0, c0, c0, 7 /* SET_WP_DBGWCR0 */ //MRC P15,0,R0,C1,C0,0 /* Enable Icache */ //orr r0,r0,#(1<<12) //MCR P15,0,R0,C1,C0,0 ldmia sp!,{r0,r1} subs pc, lr, #4 nop OptAgainLoop: b OptAgainLoop .thumb .align 4 OptAgainThumb: ldmia sp!,{r0-r3,r12,lr} OptAgainThumbLoop: b OptAgainThumbLoop isb stmdb sp!,{r0,r1} ldr r1, =DbgWCR0Save ldr r0, [r1] MCR p14, 0, R0, c0, c0, 7 /* SET_WP_DBGWCR0 */ //MRC P15,0,R0,C1,C0,0 /* Enable Icache */ //orr r0,r0,#(1<<12) //MCR P15,0,R0,C1,C0,0 ldmia sp!,{r0,r1} subs pc, lr, #4 nop
参考
1. https://cloud.tencent.com/developer/article/1578336
2. ARM Architecture Reference Manual® ARMv7-A and ARMv7-R edition