loongarch-hw-point.c revision 1.1 1 /* Native-dependent code for GNU/Linux on LoongArch processors.
2
3 Copyright (C) 2024 Free Software Foundation, Inc.
4 Contributed by Loongson Ltd.
5
6 This file is part of GDB.
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 #include "gdbsupport/break-common.h"
22 #include "gdbsupport/common-regcache.h"
23 #include "loongarch-hw-point.h"
24 #include "loongarch-linux-hw-point.h"
25
26 /* Number of hardware breakpoints/watchpoints the target supports.
27 They are initialized with values obtained via ptrace. */
28
29 int loongarch_num_bp_regs;
30 int loongarch_num_wp_regs;
31
32 /* Given the hardware breakpoint or watchpoint type TYPE and its
33 length LEN, return the expected encoding for a hardware
34 breakpoint/watchpoint control register. */
35
36 static unsigned int
37 loongarch_point_encode_ctrl_reg (enum target_hw_bp_type type, int len)
38 {
39 unsigned int ctrl, ttype, llen;
40
41 gdb_assert (len <= LOONGARCH_HWP_MAX_LEN_PER_REG);
42
43 /* type */
44 switch (type)
45 {
46 case hw_write:
47 ttype = 2;
48 break;
49 case hw_read:
50 ttype = 1;
51 break;
52 case hw_access:
53 ttype = 3;
54 break;
55 case hw_execute:
56 ttype = 0;
57 break;
58 default:
59 perror_with_name (_("Unrecognized watchpoint type"));
60 }
61
62 /* len */
63 switch (len)
64 {
65 case 1:
66 llen = 0b11;
67 break;
68 case 2:
69 llen = 0b10;
70 break;
71 case 4:
72 llen = 0b01;
73 break;
74 case 8:
75 llen = 0b00;
76 break;
77 default:
78 perror_with_name (_("Unrecognized watchpoint length"));
79 }
80 ctrl = 0;
81 if (type != hw_execute) {
82 /* type and length bitmask */
83 ctrl |= llen << 10;
84 ctrl |= ttype << 8;
85 }
86 ctrl |= CTRL_PLV3_ENABLE;
87 return ctrl;
88 }
89
90
91 /* Record the insertion of one breakpoint/watchpoint, as represented
92 by ADDR and CTRL, in the process' arch-specific data area *STATE. */
93
94 static int
95 loongarch_dr_state_insert_one_point (ptid_t ptid,
96 struct loongarch_debug_reg_state *state,
97 enum target_hw_bp_type type, CORE_ADDR addr,
98 int len, CORE_ADDR addr_orig)
99 {
100 int i, idx, num_regs, is_watchpoint;
101 unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
102 CORE_ADDR *dr_addr_p;
103
104 /* Set up state pointers. */
105 is_watchpoint = (type != hw_execute);
106 if (is_watchpoint)
107 {
108 num_regs = loongarch_num_wp_regs;
109 dr_addr_p = state->dr_addr_wp;
110 dr_ctrl_p = state->dr_ctrl_wp;
111 dr_ref_count = state->dr_ref_count_wp;
112 }
113 else
114 {
115 num_regs = loongarch_num_bp_regs;
116 dr_addr_p = state->dr_addr_bp;
117 dr_ctrl_p = state->dr_ctrl_bp;
118 dr_ref_count = state->dr_ref_count_bp;
119 }
120
121 ctrl = loongarch_point_encode_ctrl_reg (type, len);
122
123 /* Find an existing or free register in our cache. */
124 idx = -1;
125 for (i = 0; i < num_regs; ++i)
126 {
127 if ((dr_ctrl_p[i] & CTRL_PLV3_ENABLE) == 0) // PLV3 disable
128 {
129 gdb_assert (dr_ref_count[i] == 0);
130 idx = i;
131 /* no break; continue hunting for an exising one. */
132 }
133 else if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
134 {
135 idx = i;
136 gdb_assert (dr_ref_count[i] != 0);
137 break;
138 }
139
140 }
141
142 /* No space. */
143 if (idx == -1)
144 return -1;
145
146 /* Update our cache. */
147 if ((dr_ctrl_p[idx] & CTRL_PLV3_ENABLE) == 0)
148 {
149 /* new entry */
150 dr_addr_p[idx] = addr;
151 dr_ctrl_p[idx] = ctrl;
152 dr_ref_count[idx] = 1;
153
154 /* Notify the change. */
155 loongarch_notify_debug_reg_change (ptid, is_watchpoint, idx);
156 }
157 else
158 {
159 /* existing entry */
160 dr_ref_count[idx]++;
161 }
162
163 return 0;
164 }
165
166 /* Record the removal of one breakpoint/watchpoint, as represented by
167 ADDR and CTRL, in the process' arch-specific data area *STATE. */
168
169 static int
170 loongarch_dr_state_remove_one_point (ptid_t ptid,
171 struct loongarch_debug_reg_state *state,
172 enum target_hw_bp_type type, CORE_ADDR addr,
173 int len, CORE_ADDR addr_orig)
174 {
175 int i, num_regs, is_watchpoint;
176 unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
177 CORE_ADDR *dr_addr_p;
178
179 /* Set up state pointers. */
180 is_watchpoint = (type != hw_execute);
181 if (is_watchpoint)
182 {
183 num_regs = loongarch_num_wp_regs;
184 dr_addr_p = state->dr_addr_wp;
185 dr_ctrl_p = state->dr_ctrl_wp;
186 dr_ref_count = state->dr_ref_count_wp;
187 }
188 else
189 {
190 num_regs = loongarch_num_bp_regs;
191 dr_addr_p = state->dr_addr_bp;
192 dr_ctrl_p = state->dr_ctrl_bp;
193 dr_ref_count = state->dr_ref_count_bp;
194 }
195
196 ctrl = loongarch_point_encode_ctrl_reg (type, len);
197
198 /* Find the entry that matches the ADDR and CTRL. */
199 for (i = 0; i < num_regs; ++i)
200 if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
201 {
202 gdb_assert (dr_ref_count[i] != 0);
203 break;
204 }
205
206 /* Not found. */
207 if (i == num_regs)
208 return -1;
209
210 /* Clear our cache. */
211 if (--dr_ref_count[i] == 0)
212 {
213 dr_addr_p[i] = 0;
214 dr_ctrl_p[i] = 0;
215
216 /* Notify the change. */
217 loongarch_notify_debug_reg_change (ptid, is_watchpoint, i);
218 }
219
220 return 0;
221 }
222
223 int
224 loongarch_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
225 int len, int is_insert, ptid_t ptid,
226 struct loongarch_debug_reg_state *state)
227 {
228 if (is_insert)
229 return loongarch_dr_state_insert_one_point (ptid, state, type, addr,
230 len, -1);
231 else
232 return loongarch_dr_state_remove_one_point (ptid, state, type, addr,
233 len, -1);
234 }
235
236
237 int
238 loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
239 int len, int is_insert, ptid_t ptid,
240 struct loongarch_debug_reg_state *state)
241 {
242 if (is_insert)
243 return loongarch_dr_state_insert_one_point (ptid, state, type, addr,
244 len, addr);
245 else
246 return loongarch_dr_state_remove_one_point (ptid, state, type, addr,
247 len, addr);
248 }
249
250
251 /* See nat/loongarch-hw-point.h. */
252
253 bool
254 loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state,
255 bool watchpoint)
256 {
257 int count = watchpoint ? loongarch_num_wp_regs : loongarch_num_bp_regs;
258 if (count == 0)
259 return false;
260
261 const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
262 const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
263
264 for (int i = 0; i < count; i++)
265 if (addr[i] != 0 || ctrl[i] != 0)
266 return true;
267
268 return false;
269 }
270
271 /* Print the values of the cached breakpoint/watchpoint registers. */
272
273 void
274 loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state,
275 const char *func, CORE_ADDR addr,
276 int len, enum target_hw_bp_type type)
277 {
278 int i;
279
280 debug_printf ("%s", func);
281 if (addr || len)
282 debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
283 (unsigned long) addr, len,
284 type == hw_write ? "hw-write-watchpoint"
285 : (type == hw_read ? "hw-read-watchpoint"
286 : (type == hw_access ? "hw-access-watchpoint"
287 : (type == hw_execute ? "hw-breakpoint"
288 : "??unknown??"))));
289 debug_printf (":\n");
290
291 debug_printf ("\tBREAKPOINTs:\n");
292 for (i = 0; i < loongarch_num_bp_regs; i++)
293 debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
294 i, core_addr_to_string_nz (state->dr_addr_bp[i]),
295 state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
296
297 debug_printf ("\tWATCHPOINTs:\n");
298 for (i = 0; i < loongarch_num_wp_regs; i++)
299 debug_printf ("\tWP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
300 i, core_addr_to_string_nz (state->dr_addr_wp[i]),
301 state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
302 }
303
304 /* Return true if we can watch a memory region that starts address
305 ADDR and whose length is LEN in bytes. */
306
307 int
308 loongarch_region_ok_for_watchpoint (CORE_ADDR addr, int len)
309 {
310 /* Can not set watchpoints for zero or negative lengths. */
311 if (len <= 0)
312 return 0;
313
314 /* Must have hardware watchpoint debug register(s). */
315 if (loongarch_num_wp_regs == 0)
316 return 0;
317
318 return 1;
319 }
320