1 /* Native-dependent code for GNU/Linux on LoongArch processors. 2 3 Copyright (C) 2022-2023 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 "defs.h" 22 #include "elf/common.h" 23 #include "gregset.h" 24 #include "inferior.h" 25 #include "linux-nat-trad.h" 26 #include "loongarch-tdep.h" 27 #include "nat/gdb_ptrace.h" 28 #include "target-descriptions.h" 29 30 #include <asm/ptrace.h> 31 32 /* LoongArch Linux native additions to the default Linux support. */ 33 34 class loongarch_linux_nat_target final : public linux_nat_trad_target 35 { 36 public: 37 /* Add our register access methods. */ 38 void fetch_registers (struct regcache *, int) override; 39 void store_registers (struct regcache *, int) override; 40 41 protected: 42 /* Override linux_nat_trad_target methods. */ 43 CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regnum, 44 int store_p) override; 45 }; 46 47 /* Fill GDB's register array with the general-purpose, orig_a0, pc and badv 48 register values from the current thread. */ 49 50 static void 51 fetch_gregs_from_thread (struct regcache *regcache, int regnum, pid_t tid) 52 { 53 elf_gregset_t regset; 54 55 if (regnum == -1 || (regnum >= 0 && regnum < 32) 56 || regnum == LOONGARCH_ORIG_A0_REGNUM 57 || regnum == LOONGARCH_PC_REGNUM 58 || regnum == LOONGARCH_BADV_REGNUM) 59 { 60 struct iovec iov; 61 62 iov.iov_base = ®set; 63 iov.iov_len = sizeof (regset); 64 65 if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0) 66 perror_with_name (_("Couldn't get NT_PRSTATUS registers")); 67 else 68 loongarch_gregset.supply_regset (nullptr, regcache, regnum, 69 ®set, sizeof (regset)); 70 } 71 } 72 73 /* Store to the current thread the valid general-purpose, orig_a0, pc and badv 74 register values in the GDB's register array. */ 75 76 static void 77 store_gregs_to_thread (struct regcache *regcache, int regnum, pid_t tid) 78 { 79 elf_gregset_t regset; 80 81 if (regnum == -1 || (regnum >= 0 && regnum < 32) 82 || regnum == LOONGARCH_ORIG_A0_REGNUM 83 || regnum == LOONGARCH_PC_REGNUM 84 || regnum == LOONGARCH_BADV_REGNUM) 85 { 86 struct iovec iov; 87 88 iov.iov_base = ®set; 89 iov.iov_len = sizeof (regset); 90 91 if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0) 92 perror_with_name (_("Couldn't get NT_PRSTATUS registers")); 93 else 94 { 95 loongarch_gregset.collect_regset (nullptr, regcache, regnum, 96 ®set, sizeof (regset)); 97 if (ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0) 98 perror_with_name (_("Couldn't set NT_PRSTATUS registers")); 99 } 100 } 101 } 102 103 /* Fill GDB's register array with the fp, fcc and fcsr 104 register values from the current thread. */ 105 106 static void 107 fetch_fpregs_from_thread (struct regcache *regcache, int regnum, pid_t tid) 108 { 109 elf_fpregset_t regset; 110 111 if ((regnum == -1) 112 || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM)) 113 { 114 struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; 115 116 if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) 117 perror_with_name (_("Couldn't get NT_FPREGSET registers")); 118 else 119 loongarch_fpregset.supply_regset (nullptr, regcache, regnum, 120 ®set, sizeof (regset)); 121 } 122 } 123 124 /* Store to the current thread the valid fp, fcc and fcsr 125 register values in the GDB's register array. */ 126 127 static void 128 store_fpregs_to_thread (struct regcache *regcache, int regnum, pid_t tid) 129 { 130 elf_fpregset_t regset; 131 132 if ((regnum == -1) 133 || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM)) 134 { 135 struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) }; 136 137 if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) 138 perror_with_name (_("Couldn't get NT_FPREGSET registers")); 139 else 140 { 141 loongarch_fpregset.collect_regset (nullptr, regcache, regnum, 142 ®set, sizeof (regset)); 143 if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0) 144 perror_with_name (_("Couldn't set NT_FPREGSET registers")); 145 } 146 } 147 } 148 149 /* Implement the "fetch_registers" target_ops method. */ 150 151 void 152 loongarch_linux_nat_target::fetch_registers (struct regcache *regcache, 153 int regnum) 154 { 155 pid_t tid = get_ptrace_pid (regcache->ptid ()); 156 157 fetch_gregs_from_thread(regcache, regnum, tid); 158 fetch_fpregs_from_thread(regcache, regnum, tid); 159 } 160 161 /* Implement the "store_registers" target_ops method. */ 162 163 void 164 loongarch_linux_nat_target::store_registers (struct regcache *regcache, 165 int regnum) 166 { 167 pid_t tid = get_ptrace_pid (regcache->ptid ()); 168 169 store_gregs_to_thread (regcache, regnum, tid); 170 store_fpregs_to_thread(regcache, regnum, tid); 171 } 172 173 /* Return the address in the core dump or inferior of register REGNO. */ 174 175 CORE_ADDR 176 loongarch_linux_nat_target::register_u_offset (struct gdbarch *gdbarch, 177 int regnum, int store_p) 178 { 179 if (regnum >= 0 && regnum < 32) 180 return regnum; 181 else if (regnum == LOONGARCH_PC_REGNUM) 182 return LOONGARCH_PC_REGNUM; 183 else 184 return -1; 185 } 186 187 static loongarch_linux_nat_target the_loongarch_linux_nat_target; 188 189 /* Wrapper functions. These are only used by libthread_db. */ 190 191 void 192 supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregset) 193 { 194 loongarch_gregset.supply_regset (nullptr, regcache, -1, gregset, 195 sizeof (gdb_gregset_t)); 196 } 197 198 void 199 fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregset, 200 int regnum) 201 { 202 loongarch_gregset.collect_regset (nullptr, regcache, regnum, gregset, 203 sizeof (gdb_gregset_t)); 204 } 205 206 void 207 supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregset) 208 { 209 loongarch_fpregset.supply_regset (nullptr, regcache, -1, fpregset, 210 sizeof (gdb_fpregset_t)); 211 } 212 213 void 214 fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset, 215 int regnum) 216 { 217 loongarch_fpregset.collect_regset (nullptr, regcache, regnum, fpregset, 218 sizeof (gdb_fpregset_t)); 219 } 220 221 /* Initialize LoongArch Linux native support. */ 222 223 void _initialize_loongarch_linux_nat (); 224 void 225 _initialize_loongarch_linux_nat () 226 { 227 linux_target = &the_loongarch_linux_nat_target; 228 add_inf_child_target (&the_loongarch_linux_nat_target); 229 } 230