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