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 34class loongarch_linux_nat_target final : public linux_nat_trad_target 35{ 36public: 37 /* Add our register access methods. */ 38 void fetch_registers (struct regcache *, int) override; 39 void store_registers (struct regcache *, int) override; 40 41protected: 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 50static void 51fetch_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 76static void 77store_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 106static void 107fetch_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 127static void 128store_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 151void 152loongarch_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 163void 164loongarch_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 175CORE_ADDR 176loongarch_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 187static loongarch_linux_nat_target the_loongarch_linux_nat_target; 188 189/* Wrapper functions. These are only used by libthread_db. */ 190 191void 192supply_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 198void 199fill_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 206void 207supply_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 213void 214fill_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 223void _initialize_loongarch_linux_nat (); 224void 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