Home | History | Annotate | Line # | Download | only in nat
      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