xref: /openbmc/qemu/target/arm/hyp_gdbstub.c (revision 4f7b1ecb)
1 /*
2  * ARM implementation of KVM and HVF hooks, 64 bit specific code
3  *
4  * Copyright Mian-M. Hamayun 2013, Virtual Open Systems
5  * Copyright Alex Bennée 2014, Linaro
6  *
7  * This work is licensed under the terms of the GNU GPL, version 2 or later.
8  * See the COPYING file in the top-level directory.
9  *
10  */
11 
12 #include "qemu/osdep.h"
13 #include "cpu.h"
14 #include "internals.h"
15 #include "gdbstub/enums.h"
16 
17 /* Maximum and current break/watch point counts */
18 int max_hw_bps, max_hw_wps;
19 GArray *hw_breakpoints, *hw_watchpoints;
20 
21 /**
22  * insert_hw_breakpoint()
23  * @addr: address of breakpoint
24  *
25  * See ARM ARM D2.9.1 for details but here we are only going to create
26  * simple un-linked breakpoints (i.e. we don't chain breakpoints
27  * together to match address and context or vmid). The hardware is
28  * capable of fancier matching but that will require exposing that
29  * fanciness to GDB's interface
30  *
31  * DBGBCR<n>_EL1, Debug Breakpoint Control Registers
32  *
33  *  31  24 23  20 19   16 15 14  13  12   9 8   5 4    3 2   1  0
34  * +------+------+-------+-----+----+------+-----+------+-----+---+
35  * | RES0 |  BT  |  LBN  | SSC | HMC| RES0 | BAS | RES0 | PMC | E |
36  * +------+------+-------+-----+----+------+-----+------+-----+---+
37  *
38  * BT: Breakpoint type (0 = unlinked address match)
39  * LBN: Linked BP number (0 = unused)
40  * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12)
41  * BAS: Byte Address Select (RES1 for AArch64)
42  * E: Enable bit
43  *
44  * DBGBVR<n>_EL1, Debug Breakpoint Value Registers
45  *
46  *  63  53 52       49 48       2  1 0
47  * +------+-----------+----------+-----+
48  * | RESS | VA[52:49] | VA[48:2] | 0 0 |
49  * +------+-----------+----------+-----+
50  *
51  * Depending on the addressing mode bits the top bits of the register
52  * are a sign extension of the highest applicable VA bit. Some
53  * versions of GDB don't do it correctly so we ensure they are correct
54  * here so future PC comparisons will work properly.
55  */
56 
57 int insert_hw_breakpoint(target_ulong addr)
58 {
59     HWBreakpoint brk = {
60         .bcr = 0x1,                             /* BCR E=1, enable */
61         .bvr = sextract64(addr, 0, 53)
62     };
63 
64     if (cur_hw_bps >= max_hw_bps) {
65         return -ENOBUFS;
66     }
67 
68     brk.bcr = deposit32(brk.bcr, 1, 2, 0x3);   /* PMC = 11 */
69     brk.bcr = deposit32(brk.bcr, 5, 4, 0xf);   /* BAS = RES1 */
70 
71     g_array_append_val(hw_breakpoints, brk);
72 
73     return 0;
74 }
75 
76 /**
77  * delete_hw_breakpoint()
78  * @pc: address of breakpoint
79  *
80  * Delete a breakpoint and shuffle any above down
81  */
82 
83 int delete_hw_breakpoint(target_ulong pc)
84 {
85     int i;
86     for (i = 0; i < hw_breakpoints->len; i++) {
87         HWBreakpoint *brk = get_hw_bp(i);
88         if (brk->bvr == pc) {
89             g_array_remove_index(hw_breakpoints, i);
90             return 0;
91         }
92     }
93     return -ENOENT;
94 }
95 
96 /**
97  * insert_hw_watchpoint()
98  * @addr: address of watch point
99  * @len: size of area
100  * @type: type of watch point
101  *
102  * See ARM ARM D2.10. As with the breakpoints we can do some advanced
103  * stuff if we want to. The watch points can be linked with the break
104  * points above to make them context aware. However for simplicity
105  * currently we only deal with simple read/write watch points.
106  *
107  * D7.3.11 DBGWCR<n>_EL1, Debug Watchpoint Control Registers
108  *
109  *  31  29 28   24 23  21  20  19 16 15 14  13   12  5 4   3 2   1  0
110  * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
111  * | RES0 |  MASK | RES0 | WT | LBN | SSC | HMC | BAS | LSC | PAC | E |
112  * +------+-------+------+----+-----+-----+-----+-----+-----+-----+---+
113  *
114  * MASK: num bits addr mask (0=none,01/10=res,11=3 bits (8 bytes))
115  * WT: 0 - unlinked, 1 - linked (not currently used)
116  * LBN: Linked BP number (not currently used)
117  * SSC/HMC/PAC: Security, Higher and Priv access control (Table D2-11)
118  * BAS: Byte Address Select
119  * LSC: Load/Store control (01: load, 10: store, 11: both)
120  * E: Enable
121  *
122  * The bottom 2 bits of the value register are masked. Therefore to
123  * break on any sizes smaller than an unaligned word you need to set
124  * MASK=0, BAS=bit per byte in question. For larger regions (^2) you
125  * need to ensure you mask the address as required and set BAS=0xff
126  */
127 
128 int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type)
129 {
130     HWWatchpoint wp = {
131         .wcr = R_DBGWCR_E_MASK, /* E=1, enable */
132         .wvr = addr & (~0x7ULL),
133         .details = { .vaddr = addr, .len = len }
134     };
135 
136     if (cur_hw_wps >= max_hw_wps) {
137         return -ENOBUFS;
138     }
139 
140     /*
141      * HMC=0 SSC=0 PAC=3 will hit EL0 or EL1, any security state,
142      * valid whether EL3 is implemented or not
143      */
144     wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, PAC, 3);
145 
146     switch (type) {
147     case GDB_WATCHPOINT_READ:
148         wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 1);
149         wp.details.flags = BP_MEM_READ;
150         break;
151     case GDB_WATCHPOINT_WRITE:
152         wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 2);
153         wp.details.flags = BP_MEM_WRITE;
154         break;
155     case GDB_WATCHPOINT_ACCESS:
156         wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, LSC, 3);
157         wp.details.flags = BP_MEM_ACCESS;
158         break;
159     default:
160         g_assert_not_reached();
161         break;
162     }
163     if (len <= 8) {
164         /* we align the address and set the bits in BAS */
165         int off = addr & 0x7;
166         int bas = (1 << len) - 1;
167 
168         wp.wcr = deposit32(wp.wcr, 5 + off, 8 - off, bas);
169     } else {
170         /* For ranges above 8 bytes we need to be a power of 2 */
171         if (is_power_of_2(len)) {
172             int bits = ctz64(len);
173 
174             wp.wvr &= ~((1 << bits) - 1);
175             wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, MASK, bits);
176             wp.wcr = FIELD_DP64(wp.wcr, DBGWCR, BAS, 0xff);
177         } else {
178             return -ENOBUFS;
179         }
180     }
181 
182     g_array_append_val(hw_watchpoints, wp);
183     return 0;
184 }
185 
186 bool check_watchpoint_in_range(int i, target_ulong addr)
187 {
188     HWWatchpoint *wp = get_hw_wp(i);
189     uint64_t addr_top, addr_bottom = wp->wvr;
190     int bas = extract32(wp->wcr, 5, 8);
191     int mask = extract32(wp->wcr, 24, 4);
192 
193     if (mask) {
194         addr_top = addr_bottom + (1 << mask);
195     } else {
196         /*
197          * BAS must be contiguous but can offset against the base
198          * address in DBGWVR
199          */
200         addr_bottom = addr_bottom + ctz32(bas);
201         addr_top = addr_bottom + clo32(bas);
202     }
203 
204     if (addr >= addr_bottom && addr <= addr_top) {
205         return true;
206     }
207 
208     return false;
209 }
210 
211 /**
212  * delete_hw_watchpoint()
213  * @addr: address of breakpoint
214  *
215  * Delete a breakpoint and shuffle any above down
216  */
217 
218 int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type)
219 {
220     int i;
221     for (i = 0; i < cur_hw_wps; i++) {
222         if (check_watchpoint_in_range(i, addr)) {
223             g_array_remove_index(hw_watchpoints, i);
224             return 0;
225         }
226     }
227     return -ENOENT;
228 }
229 
230 bool find_hw_breakpoint(CPUState *cpu, target_ulong pc)
231 {
232     int i;
233 
234     for (i = 0; i < cur_hw_bps; i++) {
235         HWBreakpoint *bp = get_hw_bp(i);
236         if (bp->bvr == pc) {
237             return true;
238         }
239     }
240     return false;
241 }
242 
243 CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr)
244 {
245     int i;
246 
247     for (i = 0; i < cur_hw_wps; i++) {
248         if (check_watchpoint_in_range(i, addr)) {
249             return &get_hw_wp(i)->details;
250         }
251     }
252     return NULL;
253 }
254