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