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