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