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如下:

 

 

 MDBGenHDBGen分别代表了Watchpoint命中以后进入Monitor模式还是Halt模式。

如果处理器当前处于JTAG调试模式,HDBGen会自动设为1,命中以后程序会暂停,恢复以后会继续运行,不会进入异常;如果要使Watchpoint命中以后进入异常流程,需要把MDBGen设为1,并且不能使用JTAG方式进行启动。

另外如果要处理器进入DebugExeception流程,还需要操作DBGOSLAR(OS Lock Access Register )进行解锁,解锁方法是写入除了Key(0xC5ACCE55)之外的任何值,否则就是加锁。

三、设置watchpoint

 设置Watchpoint需要操作DBGWVRDBGWCR两个寄存器,寄存器功能描述见下图。

其中: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

posted @ 2022-09-21 14:31  DF11G  阅读(1258)  评论(0编辑  收藏  举报