|  | /* GNU/Linux/Microblaze specific low level interface, for the remote server for | 
|  | GDB. | 
|  | Copyright (C) 1995-2025 Free Software Foundation, Inc. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | #include "server.h" | 
|  | #include "linux-low.h" | 
|  |  | 
|  | #include "elf/common.h" | 
|  | #include "nat/gdb_ptrace.h" | 
|  | #include <endian.h> | 
|  |  | 
|  | #include <asm/ptrace.h> | 
|  | #include <sys/procfs.h> | 
|  | #include <sys/ptrace.h> | 
|  |  | 
|  | #include "gdb_proc_service.h" | 
|  |  | 
|  |  | 
|  | static int microblaze_regmap[] = | 
|  | {PT_GPR(0),     PT_GPR(1),     PT_GPR(2),     PT_GPR(3), | 
|  | PT_GPR(4),     PT_GPR(5),     PT_GPR(6),     PT_GPR(7), | 
|  | PT_GPR(8),     PT_GPR(9),     PT_GPR(10),    PT_GPR(11), | 
|  | PT_GPR(12),    PT_GPR(13),    PT_GPR(14),    PT_GPR(15), | 
|  | PT_GPR(16),    PT_GPR(17),    PT_GPR(18),    PT_GPR(19), | 
|  | PT_GPR(20),    PT_GPR(21),    PT_GPR(22),    PT_GPR(23), | 
|  | PT_GPR(24),    PT_GPR(25),    PT_GPR(26),    PT_GPR(27), | 
|  | PT_GPR(28),    PT_GPR(29),    PT_GPR(30),    PT_GPR(31), | 
|  | PT_PC,         PT_MSR,        PT_EAR,        PT_ESR, | 
|  | PT_FSR | 
|  | }; | 
|  |  | 
|  |  | 
|  |  | 
|  | class microblaze_target : public linux_process_target | 
|  | { | 
|  | public: | 
|  |  | 
|  | const regs_info *get_regs_info () override; | 
|  |  | 
|  | const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override; | 
|  |  | 
|  | protected: | 
|  |  | 
|  | void low_arch_setup () override; | 
|  |  | 
|  | bool low_cannot_fetch_register (int regno) override; | 
|  |  | 
|  | bool low_cannot_store_register (int regno) override; | 
|  |  | 
|  | bool low_supports_breakpoints () override; | 
|  |  | 
|  | CORE_ADDR low_get_pc (regcache *regcache) override; | 
|  |  | 
|  | void low_set_pc (regcache *regcache, CORE_ADDR newpc) override; | 
|  |  | 
|  | bool low_breakpoint_at (CORE_ADDR pc) override; | 
|  | }; | 
|  |  | 
|  | /* The singleton target ops object.  */ | 
|  |  | 
|  | static microblaze_target the_microblaze_target; | 
|  |  | 
|  | constexpr auto microblaze_num_regs | 
|  | = sizeof (microblaze_regmap) / sizeof (microblaze_regmap[0]); | 
|  |  | 
|  | /* Defined in auto-generated file microblaze-linux-generated.c.  */ | 
|  | void init_registers_microblaze_linux (); | 
|  | extern const target_desc *tdesc_microblaze_linux; | 
|  |  | 
|  | bool | 
|  | microblaze_target::low_supports_breakpoints () | 
|  | { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool | 
|  | microblaze_target::low_cannot_store_register (int regno) | 
|  | { | 
|  | if (microblaze_regmap[regno] == -1 || regno == 0) | 
|  | return 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool | 
|  | microblaze_target::low_cannot_fetch_register (int regno) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | CORE_ADDR | 
|  | microblaze_target::low_get_pc (regcache *regcache) | 
|  | { | 
|  | unsigned long pc; | 
|  |  | 
|  | collect_register_by_name (regcache, "rpc", &pc); | 
|  | return pc; | 
|  | } | 
|  |  | 
|  | void | 
|  | microblaze_target::low_set_pc (regcache *regcache, CORE_ADDR pc) | 
|  | { | 
|  | unsigned long newpc = pc; | 
|  |  | 
|  | supply_register_by_name (regcache, "rpc", &newpc); | 
|  | } | 
|  |  | 
|  | /* dbtrap insn */ | 
|  | /* brki r16, 0x18; */ | 
|  | static const uint32_t microblaze_breakpoint = 0xba0c0018; | 
|  | #define microblaze_breakpoint_len 4 | 
|  |  | 
|  | const gdb_byte * | 
|  | microblaze_target::sw_breakpoint_from_kind (int kind, int *size) | 
|  | { | 
|  | *size = microblaze_breakpoint_len; | 
|  | return reinterpret_cast<const gdb_byte *> (µblaze_breakpoint); | 
|  | } | 
|  |  | 
|  | bool | 
|  | microblaze_target::low_breakpoint_at (CORE_ADDR where) | 
|  | { | 
|  | uint32_t insn; | 
|  |  | 
|  | read_memory (where, (unsigned char *) &insn, 4); | 
|  | /* If necessary, recognize more trap instructions here.  GDB only uses the | 
|  | one.  */ | 
|  | return insn == microblaze_breakpoint; | 
|  | } | 
|  |  | 
|  | #ifdef HAVE_PTRACE_GETREGS | 
|  |  | 
|  | static void | 
|  | microblaze_collect_ptrace_register (struct regcache *regcache, int regno, | 
|  | char *buf) | 
|  | { | 
|  | memset (buf, 0, sizeof (long)); | 
|  |  | 
|  | if (__BYTE_ORDER == __LITTLE_ENDIAN) | 
|  | { | 
|  | collect_register (regcache, regno, buf); | 
|  | } | 
|  | else if (__BYTE_ORDER == __BIG_ENDIAN) | 
|  | { | 
|  | int size = register_size (regcache->tdesc, regno); | 
|  |  | 
|  | if (size < sizeof (long)) | 
|  | collect_register (regcache, regno, buf + sizeof (long) - size); | 
|  | else | 
|  | collect_register (regcache, regno, buf); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Collect GPRs from REGCACHE into BUF.  */ | 
|  |  | 
|  | static void microblaze_fill_gregset (struct regcache *regcache, void *buf) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < microblaze_num_regs; i++) | 
|  | microblaze_collect_ptrace_register (regcache, i, | 
|  | (char *) buf + microblaze_regmap[i]); | 
|  | } | 
|  |  | 
|  | /* Supply GPRs from BUF into REGCACHE.  */ | 
|  |  | 
|  | static void | 
|  | microblaze_store_gregset (struct regcache *regcache, const void *buf) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < microblaze_num_regs; i++) | 
|  | supply_register (regcache, i, (char *) buf + microblaze_regmap[i]); | 
|  | } | 
|  |  | 
|  | static struct regset_info microblaze_regsets[] = { | 
|  | { PTRACE_GETREGS, PTRACE_SETREGS, NT_PRSTATUS, | 
|  | sizeof (elf_gregset_t), GENERAL_REGS, | 
|  | microblaze_fill_gregset, microblaze_store_gregset | 
|  | }, | 
|  | NULL_REGSET | 
|  | }; | 
|  | #endif /* HAVE_PTRACE_GETREGS */ | 
|  |  | 
|  | static struct usrregs_info microblaze_usrregs_info = | 
|  | { | 
|  | microblaze_num_regs, | 
|  | microblaze_regmap, | 
|  | }; | 
|  |  | 
|  | #ifdef HAVE_PTRACE_GETREGS | 
|  | static struct regsets_info microblaze_regsets_info = | 
|  | { | 
|  | microblaze_regsets, /* regsets */ | 
|  | 0, /* num_regsets */ | 
|  | nullptr /* disabled_regsets */ | 
|  | }; | 
|  | #endif /* HAVE_PTRACE_GETREGS */ | 
|  |  | 
|  | static struct regs_info microblaze_regs_info = | 
|  | { | 
|  | nullptr, /* regset_bitmap */ | 
|  | µblaze_usrregs_info, | 
|  | #ifdef HAVE_PTRACE_GETREGS | 
|  | µblaze_regsets_info | 
|  | #endif /* HAVE_PTRACE_GETREGS */ | 
|  | }; | 
|  |  | 
|  | const regs_info * | 
|  | microblaze_target::get_regs_info () | 
|  | { | 
|  | return µblaze_regs_info; | 
|  | } | 
|  |  | 
|  | void | 
|  | microblaze_target::low_arch_setup () | 
|  | { | 
|  | current_process ()->tdesc = tdesc_microblaze_linux; | 
|  | } | 
|  |  | 
|  | linux_process_target *the_linux_target = &the_microblaze_target; | 
|  |  | 
|  | void | 
|  | initialize_low_arch () | 
|  | { | 
|  | init_registers_microblaze_linux (); | 
|  | #ifdef HAVE_PTRACE_GETREGS | 
|  | initialize_regsets_info (µblaze_regsets_info); | 
|  | #endif /* HAVE_PTRACE_GETREGS */ | 
|  | } |