From c7b918283f2e438deef78aae84f6b748a7a1bb54 Mon Sep 17 00:00:00 2001 From: David Holsgrove Date: Fri, 20 Jul 2012 15:18:35 +1000 Subject: [PATCH 09/16] [Patch, microblaze]: Initial support for native gdb microblaze: Follow PPC method of getting setting registers using PTRACE PEEK/POKE Signed-off-by: David Holsgrove Upstream-Status: Pending --- gdb/Makefile.in | 4 +- gdb/config/microblaze/linux.mh | 10 + gdb/microblaze-linux-nat.c | 431 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 gdb/config/microblaze/linux.mh create mode 100644 gdb/microblaze-linux-nat.c diff --git a/gdb/Makefile.in b/gdb/Makefile.in index f2c16ec..12870a4 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -904,7 +904,7 @@ top.h bsd-kvm.h gdb-stabs.h reggroups.h \ annotate.h sim-regno.h dictionary.h dfp.h main.h frame-unwind.h \ remote-fileio.h i386-linux-tdep.h vax-tdep.h objc-lang.h \ sentinel-frame.h bcache.h symfile.h windows-tdep.h linux-tdep.h \ -gdb_usleep.h jit.h xml-syscall.h microblaze-tdep.h \ +gdb_usleep.h jit.h xml-syscall.h microblaze-tdep.h microblaze-linux-tdep.h \ psymtab.h psympriv.h progspace.h bfin-tdep.h ia64-hpux-tdep.h \ amd64-darwin-tdep.h charset-list.h \ config/djgpp/langinfo.h config/djgpp/nl_types.h darwin-nat.h \ @@ -1631,7 +1631,7 @@ ALLDEPFILES = \ m68kbsd-nat.c m68kbsd-tdep.c \ m68klinux-nat.c m68klinux-tdep.c \ m88k-tdep.c m88kbsd-nat.c \ - microblaze-tdep.c microblaze-linux-tdep.c \ + microblaze-tdep.c microblaze-linux-nat.c microblaze-linux-tdep.c \ mingw-hdep.c \ mips-linux-nat.c mips-linux-tdep.c \ mips-irix-tdep.c \ diff --git a/gdb/config/microblaze/linux.mh b/gdb/config/microblaze/linux.mh new file mode 100644 index 0000000..547d103 --- /dev/null +++ b/gdb/config/microblaze/linux.mh @@ -0,0 +1,10 @@ +# Host: Microblaze, running Linux + +NAT_FILE= config/nm-linux.h +NATDEPFILES= inf-ptrace.o fork-child.o \ + microblaze-linux-nat.o proc-service.o linux-thread-db.o \ + linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \ + linux-waitpid.o +NAT_CDEPS = $(srcdir)/proc-service.list + +LOADLIBES = -ldl $(RDYNAMIC) diff --git a/gdb/microblaze-linux-nat.c b/gdb/microblaze-linux-nat.c new file mode 100644 index 0000000..81f8346 --- /dev/null +++ b/gdb/microblaze-linux-nat.c @@ -0,0 +1,431 @@ +/* Microblaze GNU/Linux native support. + + Copyright (C) 1988-1989, 1991-1992, 1994, 1996, 2000-2012 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 . */ + +#include "defs.h" +#include "arch-utils.h" +#include "dis-asm.h" +#include "frame.h" +#include "trad-frame.h" +#include "symtab.h" +#include "value.h" +#include "gdbcmd.h" +#include "breakpoint.h" +#include "inferior.h" +#include "regcache.h" +#include "target.h" +#include "frame.h" +#include "frame-base.h" +#include "frame-unwind.h" +#include "dwarf2-frame.h" +#include "osabi.h" + +#include "gdb_assert.h" +#include +#include "target-descriptions.h" +#include "opcodes/microblaze-opcm.h" +#include "opcodes/microblaze-dis.h" + +#include "linux-nat.h" +#include "target-descriptions.h" + +#include +#include +#include +#include + +/* Prototypes for supply_gregset etc. */ +#include "gregset.h" + +#include "microblaze-tdep.h" + +#include +#include "auxv.h" + +/* Defines ps_err_e, struct ps_prochandle. */ +#include "gdb_proc_service.h" + +/* On GNU/Linux, threads are implemented as pseudo-processes, in which + case we may be tracing more than one process at a time. In that + case, inferior_ptid will contain the main process ID and the + individual thread (process) ID. get_thread_id () is used to get + the thread id if it's available, and the process id otherwise. */ + +int +get_thread_id (ptid_t ptid) +{ + int tid = ptid_get_lwp (ptid); + if (0 == tid) + tid = ptid_get_pid (ptid); + return tid; +} + +#define GET_THREAD_ID(PTID) get_thread_id (PTID) + +/* Non-zero if our kernel may support the PTRACE_GETREGS and + PTRACE_SETREGS requests, for reading and writing the + general-purpose registers. Zero if we've tried one of + them and gotten an error. */ +int have_ptrace_getsetregs = 1; + +static int +microblaze_register_u_addr (struct gdbarch *gdbarch, int regno) +{ + int u_addr = -1; + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + /* NOTE: cagney/2003-11-25: This is the word size used by the ptrace + interface, and not the wordsize of the program's ABI. */ + int wordsize = sizeof (long); + + /* General purpose registers occupy 1 slot each in the buffer. */ + if (regno >= MICROBLAZE_R0_REGNUM + && regno <= MICROBLAZE_FSR_REGNUM) + u_addr = (regno * wordsize); + + return u_addr; +} + + +static void +fetch_register (struct regcache *regcache, int tid, int regno) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + /* This isn't really an address. But ptrace thinks of it as one. */ + CORE_ADDR regaddr = microblaze_register_u_addr (gdbarch, regno); + int bytes_transferred; + unsigned int offset; /* Offset of registers within the u area. */ + char buf[MAX_REGISTER_SIZE]; + + if (regaddr == -1) + { + memset (buf, '\0', register_size (gdbarch, regno)); /* Supply zeroes */ + regcache_raw_supply (regcache, regno, buf); + return; + } + + /* Read the raw register using sizeof(long) sized chunks. On a + 32-bit platform, 64-bit floating-point registers will require two + transfers. */ + for (bytes_transferred = 0; + bytes_transferred < register_size (gdbarch, regno); + bytes_transferred += sizeof (long)) + { + long l; + + errno = 0; + l = ptrace (PTRACE_PEEKUSER, tid, (PTRACE_TYPE_ARG3) regaddr, 0); + regaddr += sizeof (long); + if (errno != 0) + { + char message[128]; + sprintf (message, "reading register %s (#%d)", + gdbarch_register_name (gdbarch, regno), regno); + perror_with_name (message); + } + memcpy (&buf[bytes_transferred], &l, sizeof (l)); + } + + /* Now supply the register. Keep in mind that the regcache's idea + of the register's size may not be a multiple of sizeof + (long). */ + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) + { + /* Little-endian values are always found at the left end of the + bytes transferred. */ + regcache_raw_supply (regcache, regno, buf); + } + else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + { + /* Big-endian values are found at the right end of the bytes + transferred. */ + size_t padding = (bytes_transferred - register_size (gdbarch, regno)); + regcache_raw_supply (regcache, regno, buf + padding); + } + else + internal_error (__FILE__, __LINE__, + _("fetch_register: unexpected byte order: %d"), + gdbarch_byte_order (gdbarch)); +} + +/* This function actually issues the request to ptrace, telling + it to get all general-purpose registers and put them into the + specified regset. + + If the ptrace request does not exist, this function returns 0 + and properly sets the have_ptrace_* flag. If the request fails, + this function calls perror_with_name. Otherwise, if the request + succeeds, then the regcache gets filled and 1 is returned. */ +static int +fetch_all_gp_regs (struct regcache *regcache, int tid) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + gdb_gregset_t gregset; + + if (ptrace (PTRACE_GETREGS, tid, 0, (void *) &gregset) < 0) + { + if (errno == EIO) + { + have_ptrace_getsetregs = 0; + return 0; + } + perror_with_name (_("Couldn't get general-purpose registers.")); + } + + supply_gregset (regcache, (const gdb_gregset_t *) &gregset); + + return 1; +} + + +/* This is a wrapper for the fetch_all_gp_regs function. It is + responsible for verifying if this target has the ptrace request + that can be used to fetch all general-purpose registers at one + shot. If it doesn't, then we should fetch them using the + old-fashioned way, which is to iterate over the registers and + request them one by one. */ +static void +fetch_gp_regs (struct regcache *regcache, int tid) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int i; + + if (have_ptrace_getsetregs) + if (fetch_all_gp_regs (regcache, tid)) + return; + + /* If we've hit this point, it doesn't really matter which + architecture we are using. We just need to read the + registers in the "old-fashioned way". */ + for (i = MICROBLAZE_R0_REGNUM; i <= MICROBLAZE_FSR_REGNUM; i++) + fetch_register (regcache, tid, i); +} + + +static void +store_register (const struct regcache *regcache, int tid, int regno) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + /* This isn't really an address. But ptrace thinks of it as one. */ + CORE_ADDR regaddr = microblaze_register_u_addr (gdbarch, regno); + int i; + size_t bytes_to_transfer; + char buf[MAX_REGISTER_SIZE]; + + if (regaddr == -1) + return; + + /* First collect the register. Keep in mind that the regcache's + idea of the register's size may not be a multiple of sizeof + (long). */ + memset (buf, 0, sizeof buf); + bytes_to_transfer = align_up (register_size (gdbarch, regno), sizeof (long)); + if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) + { + /* Little-endian values always sit at the left end of the buffer. */ + regcache_raw_collect (regcache, regno, buf); + } + else if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + { + /* Big-endian values sit at the right end of the buffer. */ + size_t padding = (bytes_to_transfer - register_size (gdbarch, regno)); + regcache_raw_collect (regcache, regno, buf + padding); + } + + for (i = 0; i < bytes_to_transfer; i += sizeof (long)) + { + long l; + + memcpy (&l, &buf[i], sizeof (l)); + errno = 0; + ptrace (PTRACE_POKEUSER, tid, (PTRACE_TYPE_ARG3) regaddr, l); + regaddr += sizeof (long); + + if (errno != 0) + { + char message[128]; + sprintf (message, "writing register %s (#%d)", + gdbarch_register_name (gdbarch, regno), regno); + perror_with_name (message); + } + } +} + +/* This function actually issues the request to ptrace, telling + it to store all general-purpose registers present in the specified + regset. + + If the ptrace request does not exist, this function returns 0 + and properly sets the have_ptrace_* flag. If the request fails, + this function calls perror_with_name. Otherwise, if the request + succeeds, then the regcache is stored and 1 is returned. */ +static int +store_all_gp_regs (const struct regcache *regcache, int tid, int regno) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + gdb_gregset_t gregset; + + if (ptrace (PTRACE_GETREGS, tid, 0, (void *) &gregset) < 0) + { + if (errno == EIO) + { + have_ptrace_getsetregs = 0; + return 0; + } + perror_with_name (_("Couldn't get general-purpose registers.")); + } + + fill_gregset (regcache, &gregset, regno); + + if (ptrace (PTRACE_SETREGS, tid, 0, (void *) &gregset) < 0) + { + if (errno == EIO) + { + have_ptrace_getsetregs = 0; + return 0; + } + perror_with_name (_("Couldn't set general-purpose registers.")); + } + + return 1; +} + +/* This is a wrapper for the store_all_gp_regs function. It is + responsible for verifying if this target has the ptrace request + that can be used to store all general-purpose registers at one + shot. If it doesn't, then we should store them using the + old-fashioned way, which is to iterate over the registers and + store them one by one. */ +static void +store_gp_regs (const struct regcache *regcache, int tid, int regno) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int i; + + if (have_ptrace_getsetregs) + if (store_all_gp_regs (regcache, tid, regno)) + return; + + /* If we hit this point, it doesn't really matter which + architecture we are using. We just need to store the + registers in the "old-fashioned way". */ + for (i = MICROBLAZE_R0_REGNUM; i <= MICROBLAZE_FSR_REGNUM; i++) + store_register (regcache, tid, i); +} + + +/* Fetch registers from the child process. Fetch all registers if + regno == -1, otherwise fetch all general registers or all floating + point registers depending upon the value of regno. */ + +static void +microblaze_linux_fetch_inferior_registers (struct target_ops *ops, + struct regcache *regcache, int regno) +{ + /* Get the thread id for the ptrace call. */ + int tid = GET_THREAD_ID (inferior_ptid); + + if (regno == -1) + fetch_gp_regs (regcache, tid); + else + fetch_register (regcache, tid, regno); +} + +/* Store registers back into the inferior. Store all registers if + regno == -1, otherwise store all general registers or all floating + point registers depending upon the value of regno. */ + +static void +microblaze_linux_store_inferior_registers (struct target_ops *ops, + struct regcache *regcache, int regno) +{ + /* Get the thread id for the ptrace call. */ + int tid = GET_THREAD_ID (inferior_ptid); + + if (regno >= 0) + store_register (regcache, tid, regno); + else + store_gp_regs (regcache, tid, -1); +} + +/* Wrapper functions for the standard regset handling, used by + thread debugging. */ + +void +fill_gregset (const struct regcache *regcache, + gdb_gregset_t *gregsetp, int regno) +{ + microblaze_collect_gregset (NULL, regcache, regno, gregsetp); +} + +void +supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp) +{ + microblaze_supply_gregset (NULL, regcache, -1, gregsetp); +} + +void +fill_fpregset (const struct regcache *regcache, + gdb_fpregset_t *fpregsetp, int regno) +{ + /* FIXME. */ +} + +void +supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp) +{ + /* FIXME. */ +} + +static const struct target_desc * +microblaze_linux_read_description (struct target_ops *ops) +{ + CORE_ADDR microblaze_hwcap = 0; + + if (target_auxv_search (ops, AT_HWCAP, µblaze_hwcap) != 1) + return NULL; + + return NULL; +} + + +void _initialize_microblaze_linux_nat (void); + +void +_initialize_microblaze_linux_nat (void) +{ + struct target_ops *t; + + /* Fill in the generic GNU/Linux methods. */ + t = linux_target (); + + /* Add our register access methods. */ + t->to_fetch_registers = microblaze_linux_fetch_inferior_registers; + t->to_store_registers = microblaze_linux_store_inferior_registers; + + t->to_read_description = microblaze_linux_read_description; + + /* Register the target. */ + linux_nat_add_target (t); +} -- 1.9.0