1 /* GNU/Linux/RISC-V specific low level interface, for the remote server 2 for GDB. 3 Copyright (C) 2020-2023 Free Software Foundation, Inc. 4 5 This file is part of GDB. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #include "server.h" 21 22 #include "linux-low.h" 23 #include "tdesc.h" 24 #include "elf/common.h" 25 #include "nat/riscv-linux-tdesc.h" 26 #include "opcode/riscv.h" 27 28 /* Work around glibc header breakage causing ELF_NFPREG not to be usable. */ 29 #ifndef NFPREG 30 # define NFPREG 33 31 #endif 32 33 /* Linux target op definitions for the RISC-V architecture. */ 34 35 class riscv_target : public linux_process_target 36 { 37 public: 38 39 const regs_info *get_regs_info () override; 40 41 int breakpoint_kind_from_pc (CORE_ADDR *pcptr) override; 42 43 const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override; 44 45 protected: 46 47 void low_arch_setup () override; 48 49 bool low_cannot_fetch_register (int regno) override; 50 51 bool low_cannot_store_register (int regno) override; 52 53 bool low_fetch_register (regcache *regcache, int regno) override; 54 55 bool low_supports_breakpoints () override; 56 57 CORE_ADDR low_get_pc (regcache *regcache) override; 58 59 void low_set_pc (regcache *regcache, CORE_ADDR newpc) override; 60 61 bool low_breakpoint_at (CORE_ADDR pc) override; 62 }; 63 64 /* The singleton target ops object. */ 65 66 static riscv_target the_riscv_target; 67 68 bool 69 riscv_target::low_cannot_fetch_register (int regno) 70 { 71 gdb_assert_not_reached ("linux target op low_cannot_fetch_register " 72 "is not implemented by the target"); 73 } 74 75 bool 76 riscv_target::low_cannot_store_register (int regno) 77 { 78 gdb_assert_not_reached ("linux target op low_cannot_store_register " 79 "is not implemented by the target"); 80 } 81 82 /* Implementation of linux target ops method "low_arch_setup". */ 83 84 void 85 riscv_target::low_arch_setup () 86 { 87 static const char *expedite_regs[] = { "sp", "pc", NULL }; 88 89 const riscv_gdbarch_features features 90 = riscv_linux_read_features (lwpid_of (current_thread)); 91 target_desc_up tdesc = riscv_create_target_description (features); 92 93 if (!tdesc->expedite_regs) 94 init_target_desc (tdesc.get (), expedite_regs); 95 current_process ()->tdesc = tdesc.release (); 96 } 97 98 /* Collect GPRs from REGCACHE into BUF. */ 99 100 static void 101 riscv_fill_gregset (struct regcache *regcache, void *buf) 102 { 103 const struct target_desc *tdesc = regcache->tdesc; 104 elf_gregset_t *regset = (elf_gregset_t *) buf; 105 int regno = find_regno (tdesc, "zero"); 106 int i; 107 108 collect_register_by_name (regcache, "pc", *regset); 109 for (i = 1; i < ARRAY_SIZE (*regset); i++) 110 collect_register (regcache, regno + i, *regset + i); 111 } 112 113 /* Supply GPRs from BUF into REGCACHE. */ 114 115 static void 116 riscv_store_gregset (struct regcache *regcache, const void *buf) 117 { 118 const elf_gregset_t *regset = (const elf_gregset_t *) buf; 119 const struct target_desc *tdesc = regcache->tdesc; 120 int regno = find_regno (tdesc, "zero"); 121 int i; 122 123 supply_register_by_name (regcache, "pc", *regset); 124 supply_register_zeroed (regcache, regno); 125 for (i = 1; i < ARRAY_SIZE (*regset); i++) 126 supply_register (regcache, regno + i, *regset + i); 127 } 128 129 /* Collect FPRs from REGCACHE into BUF. */ 130 131 static void 132 riscv_fill_fpregset (struct regcache *regcache, void *buf) 133 { 134 const struct target_desc *tdesc = regcache->tdesc; 135 int regno = find_regno (tdesc, "ft0"); 136 int flen = register_size (regcache->tdesc, regno); 137 gdb_byte *regbuf = (gdb_byte *) buf; 138 int i; 139 140 for (i = 0; i < ELF_NFPREG - 1; i++, regbuf += flen) 141 collect_register (regcache, regno + i, regbuf); 142 collect_register_by_name (regcache, "fcsr", regbuf); 143 } 144 145 /* Supply FPRs from BUF into REGCACHE. */ 146 147 static void 148 riscv_store_fpregset (struct regcache *regcache, const void *buf) 149 { 150 const struct target_desc *tdesc = regcache->tdesc; 151 int regno = find_regno (tdesc, "ft0"); 152 int flen = register_size (regcache->tdesc, regno); 153 const gdb_byte *regbuf = (const gdb_byte *) buf; 154 int i; 155 156 for (i = 0; i < ELF_NFPREG - 1; i++, regbuf += flen) 157 supply_register (regcache, regno + i, regbuf); 158 supply_register_by_name (regcache, "fcsr", regbuf); 159 } 160 161 /* RISC-V/Linux regsets. FPRs are optional and come in different sizes, 162 so define multiple regsets for them marking them all as OPTIONAL_REGS 163 rather than FP_REGS, so that "regsets_fetch_inferior_registers" picks 164 the right one according to size. */ 165 static struct regset_info riscv_regsets[] = { 166 { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, 167 sizeof (elf_gregset_t), GENERAL_REGS, 168 riscv_fill_gregset, riscv_store_gregset }, 169 { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, 170 sizeof (struct __riscv_mc_q_ext_state), OPTIONAL_REGS, 171 riscv_fill_fpregset, riscv_store_fpregset }, 172 { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, 173 sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS, 174 riscv_fill_fpregset, riscv_store_fpregset }, 175 { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, 176 sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS, 177 riscv_fill_fpregset, riscv_store_fpregset }, 178 NULL_REGSET 179 }; 180 181 /* RISC-V/Linux regset information. */ 182 static struct regsets_info riscv_regsets_info = 183 { 184 riscv_regsets, /* regsets */ 185 0, /* num_regsets */ 186 NULL, /* disabled_regsets */ 187 }; 188 189 /* Definition of linux_target_ops data member "regs_info". */ 190 static struct regs_info riscv_regs = 191 { 192 NULL, /* regset_bitmap */ 193 NULL, /* usrregs */ 194 &riscv_regsets_info, 195 }; 196 197 /* Implementation of linux target ops method "get_regs_info". */ 198 199 const regs_info * 200 riscv_target::get_regs_info () 201 { 202 return &riscv_regs; 203 } 204 205 /* Implementation of linux target ops method "low_fetch_register". */ 206 207 bool 208 riscv_target::low_fetch_register (regcache *regcache, int regno) 209 { 210 const struct target_desc *tdesc = regcache->tdesc; 211 212 if (regno != find_regno (tdesc, "zero")) 213 return false; 214 supply_register_zeroed (regcache, regno); 215 return true; 216 } 217 218 bool 219 riscv_target::low_supports_breakpoints () 220 { 221 return true; 222 } 223 224 /* Implementation of linux target ops method "low_get_pc". */ 225 226 CORE_ADDR 227 riscv_target::low_get_pc (regcache *regcache) 228 { 229 elf_gregset_t regset; 230 231 if (sizeof (regset[0]) == 8) 232 return linux_get_pc_64bit (regcache); 233 else 234 return linux_get_pc_32bit (regcache); 235 } 236 237 /* Implementation of linux target ops method "low_set_pc". */ 238 239 void 240 riscv_target::low_set_pc (regcache *regcache, CORE_ADDR newpc) 241 { 242 elf_gregset_t regset; 243 244 if (sizeof (regset[0]) == 8) 245 linux_set_pc_64bit (regcache, newpc); 246 else 247 linux_set_pc_32bit (regcache, newpc); 248 } 249 250 /* Correct in either endianness. */ 251 static const uint16_t riscv_ibreakpoint[] = { 0x0073, 0x0010 }; 252 static const uint16_t riscv_cbreakpoint = 0x9002; 253 254 /* Implementation of target ops method "breakpoint_kind_from_pc". */ 255 256 int 257 riscv_target::breakpoint_kind_from_pc (CORE_ADDR *pcptr) 258 { 259 union 260 { 261 gdb_byte bytes[2]; 262 uint16_t insn; 263 } 264 buf; 265 266 if (target_read_memory (*pcptr, buf.bytes, sizeof (buf.insn)) == 0 267 && riscv_insn_length (buf.insn == sizeof (riscv_ibreakpoint))) 268 return sizeof (riscv_ibreakpoint); 269 else 270 return sizeof (riscv_cbreakpoint); 271 } 272 273 /* Implementation of target ops method "sw_breakpoint_from_kind". */ 274 275 const gdb_byte * 276 riscv_target::sw_breakpoint_from_kind (int kind, int *size) 277 { 278 *size = kind; 279 switch (kind) 280 { 281 case sizeof (riscv_ibreakpoint): 282 return (const gdb_byte *) &riscv_ibreakpoint; 283 default: 284 return (const gdb_byte *) &riscv_cbreakpoint; 285 } 286 } 287 288 /* Implementation of linux target ops method "low_breakpoint_at". */ 289 290 bool 291 riscv_target::low_breakpoint_at (CORE_ADDR pc) 292 { 293 union 294 { 295 gdb_byte bytes[2]; 296 uint16_t insn; 297 } 298 buf; 299 300 if (target_read_memory (pc, buf.bytes, sizeof (buf.insn)) == 0 301 && (buf.insn == riscv_cbreakpoint 302 || (buf.insn == riscv_ibreakpoint[0] 303 && target_read_memory (pc + sizeof (buf.insn), buf.bytes, 304 sizeof (buf.insn)) == 0 305 && buf.insn == riscv_ibreakpoint[1]))) 306 return true; 307 else 308 return false; 309 } 310 311 /* The linux target ops object. */ 312 313 linux_process_target *the_linux_target = &the_riscv_target; 314 315 /* Initialize the RISC-V/Linux target. */ 316 317 void 318 initialize_low_arch () 319 { 320 initialize_regsets_info (&riscv_regsets_info); 321 } 322