/* PPC GNU/Linux native support.
- Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 1988-2013 Free Software Foundation, Inc.
This file is part of GDB.
typedef char gdb_vsxregset_t[SIZEOF_VSXREGS];
-/* On PPC processors that support the the Signal Processing Extension
+/* On PPC processors that support the Signal Processing Extension
(SPE) APU, the general-purpose registers are 64 bits long.
However, the ordinary Linux kernel PTRACE_PEEKUSER / PTRACE_POKEUSER
ptrace calls only access the lower half of each register, to allow
GDB itself continues to claim the general-purpose registers are 32
bits long. It has unnamed raw registers that hold the upper halves
- of the gprs, and the the full 64-bit SIMD views of the registers,
+ of the gprs, and the full 64-bit SIMD views of the registers,
'ev0' -- 'ev31', are pseudo-registers that splice the top and
bottom halves together.
CORE_ADDR regaddr = ppc_register_u_addr (gdbarch, regno);
int bytes_transferred;
unsigned int offset; /* Offset of registers within the u area. */
- char buf[MAX_REGISTER_SIZE];
+ gdb_byte buf[MAX_REGISTER_SIZE];
if (altivec_register_p (gdbarch, regno))
{
bytes_transferred < register_size (gdbarch, regno);
bytes_transferred += sizeof (long))
{
+ long l;
+
errno = 0;
- *(long *) &buf[bytes_transferred]
- = ptrace (PTRACE_PEEKUSER, tid, (PTRACE_TYPE_ARG3) regaddr, 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);
+ xsnprintf (message, sizeof (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
CORE_ADDR regaddr = ppc_register_u_addr (gdbarch, regno);
int i;
size_t bytes_to_transfer;
- char buf[MAX_REGISTER_SIZE];
+ gdb_byte buf[MAX_REGISTER_SIZE];
if (altivec_register_p (gdbarch, regno))
{
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,
- *(long *) &buf[i]);
+ ptrace (PTRACE_POKEUSER, tid, (PTRACE_TYPE_ARG3) regaddr, l);
regaddr += sizeof (long);
if (errno == EIO
if (errno != 0)
{
char message[128];
- sprintf (message, "writing register %s (#%d)",
- gdbarch_register_name (gdbarch, regno), regno);
+ xsnprintf (message, sizeof (message), "writing register %s (#%d)",
+ gdbarch_register_name (gdbarch, regno), regno);
perror_with_name (message);
}
}
}
/* Fetch the AT_HWCAP entry from the aux vector. */
-unsigned long ppc_linux_get_hwcap (void)
+static unsigned long
+ppc_linux_get_hwcap (void)
{
CORE_ADDR field;
/* Check for kernel support for BOOKE debug registers. */
if (ptrace (PPC_PTRACE_GETHWDBGINFO, tid, 0, &booke_debug_info) >= 0)
{
- have_ptrace_booke_interface = 1;
- max_slots_number = booke_debug_info.num_instruction_bps
- + booke_debug_info.num_data_bps
- + booke_debug_info.num_condition_regs;
- }
- else
- {
- /* Old school interface and no BOOKE debug registers support. */
- have_ptrace_booke_interface = 0;
- memset (&booke_debug_info, 0, sizeof (struct ppc_debug_info));
+ /* Check whether ptrace BOOKE interface is functional and
+ provides any supported feature. */
+ if (booke_debug_info.features != 0)
+ {
+ have_ptrace_booke_interface = 1;
+ max_slots_number = booke_debug_info.num_instruction_bps
+ + booke_debug_info.num_data_bps
+ + booke_debug_info.num_condition_regs;
+ return have_ptrace_booke_interface;
+ }
}
+ /* Old school interface and no BOOKE debug registers support. */
+ have_ptrace_booke_interface = 0;
+ memset (&booke_debug_info, 0, sizeof (struct ppc_debug_info));
}
return have_ptrace_booke_interface;
if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
|| type == bp_access_watchpoint || type == bp_watchpoint)
{
- if (cnt > total_hw_wp)
+ if (cnt + ot > total_hw_wp)
return -1;
}
else if (type == bp_hardware_breakpoint)
to determine the hardcoded watchable region for watchpoints. */
if (have_ptrace_booke_interface ())
{
- /* DAC-based processors (i.e., embedded processors), like the PowerPC 440
- have ranged watchpoints and can watch any access within an arbitrary
- memory region. This is useful to watch arrays and structs, for
- instance. It takes two hardware watchpoints though. */
+ /* Embedded DAC-based processors, like the PowerPC 440 have ranged
+ watchpoints and can watch any access within an arbitrary memory
+ region. This is useful to watch arrays and structs, for instance. It
+ takes two hardware watchpoints though. */
if (len > 1
- && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE
+ && ppc_linux_get_hwcap () & PPC_FEATURE_BOOKE)
return 2;
- else if (booke_debug_info.data_bp_alignment
- && (addr + len > (addr & ~(booke_debug_info.data_bp_alignment - 1))
- + booke_debug_info.data_bp_alignment))
+ /* Server processors provide one hardware watchpoint and addr+len should
+ fall in the watchable region provided by the ptrace interface. */
+ if (booke_debug_info.data_bp_alignment
+ && (addr + len > (addr & ~(booke_debug_info.data_bp_alignment - 1))
+ + booke_debug_info.data_bp_alignment))
return 0;
}
/* addr+len must fall in the 8 byte watchable region for DABR-based
hw_breaks[i].hw_break = NULL;
}
+/* Return the number of registers needed for a ranged breakpoint. */
+
+static int
+ppc_linux_ranged_break_num_registers (struct target_ops *target)
+{
+ return ((have_ptrace_booke_interface ()
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_INSN_BP_RANGE)?
+ 2 : -1);
+}
+
+/* Insert the hardware breakpoint described by BP_TGT. Returns 0 for
+ success, 1 if hardware breakpoints are not supported or -1 for failure. */
+
static int
ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- ptid_t ptid;
struct lwp_info *lp;
struct ppc_hw_breakpoint p;
p.version = PPC_DEBUG_CURRENT_VERSION;
p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
- p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
p.addr = (uint64_t) bp_tgt->placed_address;
- p.addr2 = 0;
p.condition_value = 0;
- ALL_LWPS (lp, ptid)
- booke_insert_point (&p, TIDGET (ptid));
+ if (bp_tgt->length)
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+
+ /* The breakpoint will trigger if the address of the instruction is
+ within the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ }
+ else
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.addr2 = 0;
+ }
+
+ ALL_LWPS (lp)
+ booke_insert_point (&p, TIDGET (lp->ptid));
return 0;
}
ppc_linux_remove_hw_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
{
- ptid_t ptid;
struct lwp_info *lp;
struct ppc_hw_breakpoint p;
p.version = PPC_DEBUG_CURRENT_VERSION;
p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
- p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
p.addr = (uint64_t) bp_tgt->placed_address;
- p.addr2 = 0;
p.condition_value = 0;
- ALL_LWPS (lp, ptid)
- booke_remove_point (&p, TIDGET (ptid));
+ if (bp_tgt->length)
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+
+ /* The breakpoint will trigger if the address of the instruction is within
+ the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ }
+ else
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.addr2 = 0;
+ }
+
+ ALL_LWPS (lp)
+ booke_remove_point (&p, TIDGET (lp->ptid));
return 0;
}
return t;
}
+/* Insert a new masked watchpoint at ADDR using the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 on success and throws
+ an error on failure. */
+
+static int
+ppc_linux_insert_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
+ CORE_ADDR mask, int rw)
+{
+ struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
+
+ gdb_assert (have_ptrace_booke_interface ());
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = get_trigger_type (rw);
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = addr;
+ p.addr2 = mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp)
+ booke_insert_point (&p, TIDGET (lp->ptid));
+
+ return 0;
+}
+
+/* Remove a masked watchpoint at ADDR with the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 on success and throws
+ an error on failure. */
+
+static int
+ppc_linux_remove_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
+ CORE_ADDR mask, int rw)
+{
+ struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
+
+ gdb_assert (have_ptrace_booke_interface ());
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = get_trigger_type (rw);
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = addr;
+ p.addr2 = mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp)
+ booke_remove_point (&p, TIDGET (lp->ptid));
+
+ return 0;
+}
+
/* Check whether we have at least one free DVC register. */
static int
can_use_watchpoint_cond_accel (void)
DVC (Data Value Compare) register in BookE processors. The expression
must test the watch value for equality with a constant expression.
If the function returns 1, DATA_VALUE will contain the constant against
- which the watch value should be compared. */
+ which the watch value should be compared and LEN will contain the size
+ of the constant. */
static int
check_condition (CORE_ADDR watch_addr, struct expression *cond,
- CORE_ADDR *data_value)
+ CORE_ADDR *data_value, int *len)
{
int pc = 1, num_accesses_left, num_accesses_right;
struct value *left_val, *right_val, *left_chain, *right_chain;
if (num_accesses_left == 1 && num_accesses_right == 0
&& VALUE_LVAL (left_val) == lval_memory
&& value_address (left_val) == watch_addr)
- *data_value = value_as_long (right_val);
+ {
+ *data_value = value_as_long (right_val);
+
+ /* DATA_VALUE is the constant in RIGHT_VAL, but actually has
+ the same type as the memory region referenced by LEFT_VAL. */
+ *len = TYPE_LENGTH (check_typedef (value_type (left_val)));
+ }
else if (num_accesses_left == 0 && num_accesses_right == 1
&& VALUE_LVAL (right_val) == lval_memory
&& value_address (right_val) == watch_addr)
- *data_value = value_as_long (left_val);
+ {
+ *data_value = value_as_long (left_val);
+
+ /* DATA_VALUE is the constant in LEFT_VAL, but actually has
+ the same type as the memory region referenced by RIGHT_VAL. */
+ *len = TYPE_LENGTH (check_typedef (value_type (right_val)));
+ }
else
{
free_value_chain (left_chain);
return (have_ptrace_booke_interface ()
&& booke_debug_info.num_condition_regs > 0
- && check_condition (addr, cond, &data_value));
+ && check_condition (addr, cond, &data_value, &len));
}
/* Set up P with the parameters necessary to request a watchpoint covering
int len, int rw, struct expression *cond,
int insert)
{
- if (len == 1)
+ if (len == 1
+ || !(booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
{
int use_condition;
CORE_ADDR data_value;
use_condition = (insert? can_use_watchpoint_cond_accel ()
: booke_debug_info.num_condition_regs > 0);
- if (cond && use_condition && check_condition (addr, cond, &data_value))
+ if (cond && use_condition && check_condition (addr, cond,
+ &data_value, &len))
calculate_dvc (addr, len, data_value, &p->condition_mode,
&p->condition_value);
else
struct expression *cond)
{
struct lwp_info *lp;
- ptid_t ptid;
int ret = -1;
if (have_ptrace_booke_interface ())
create_watchpoint_request (&p, addr, len, rw, cond, 1);
- ALL_LWPS (lp, ptid)
- booke_insert_point (&p, TIDGET (ptid));
+ ALL_LWPS (lp)
+ booke_insert_point (&p, TIDGET (lp->ptid));
ret = 0;
}
saved_dabr_value = dabr_value;
- ALL_LWPS (lp, ptid)
- if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0,
+ ALL_LWPS (lp)
+ if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (lp->ptid), 0,
saved_dabr_value) < 0)
return -1;
struct expression *cond)
{
struct lwp_info *lp;
- ptid_t ptid;
int ret = -1;
if (have_ptrace_booke_interface ())
create_watchpoint_request (&p, addr, len, rw, cond, 0);
- ALL_LWPS (lp, ptid)
- booke_remove_point (&p, TIDGET (ptid));
+ ALL_LWPS (lp)
+ booke_remove_point (&p, TIDGET (lp->ptid));
ret = 0;
}
else
{
saved_dabr_value = 0;
- ALL_LWPS (lp, ptid)
- if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (ptid), 0,
+ ALL_LWPS (lp)
+ if (ptrace (PTRACE_SET_DEBUGREG, TIDGET (lp->ptid), 0,
saved_dabr_value) < 0)
return -1;
}
static void
-ppc_linux_new_thread (ptid_t ptid)
+ppc_linux_new_thread (struct lwp_info *lp)
{
- int tid = TIDGET (ptid);
+ int tid = TIDGET (lp->ptid);
if (have_ptrace_booke_interface ())
{
/* Copy that thread's breakpoints and watchpoints to the new thread. */
for (i = 0; i < max_slots_number; i++)
if (hw_breaks[i].hw_break)
- booke_insert_point (hw_breaks[i].hw_break, tid);
+ {
+ /* Older kernels did not make new threads inherit their parent
+ thread's debug state, so we always clear the slot and replicate
+ the debug state ourselves, ensuring compatibility with all
+ kernels. */
+
+ /* The ppc debug resource accounting is done through "slots".
+ Ask the kernel the deallocate this specific *point's slot. */
+ ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot);
+
+ booke_insert_point (hw_breaks[i].hw_break, tid);
+ }
}
else
ptrace (PTRACE_SET_DEBUGREG, tid, 0, saved_dabr_value);
static int
ppc_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
{
- struct siginfo *siginfo_p;
+ siginfo_t siginfo;
- siginfo_p = linux_nat_get_siginfo (inferior_ptid);
+ if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
+ return 0;
- if (siginfo_p->si_signo != SIGTRAP
- || (siginfo_p->si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
+ if (siginfo.si_signo != SIGTRAP
+ || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
return 0;
if (have_ptrace_booke_interface ())
struct thread_points *t;
struct hw_break_tuple *hw_breaks;
/* The index (or slot) of the *point is passed in the si_errno field. */
- int slot = siginfo_p->si_errno;
+ int slot = siginfo.si_errno;
t = booke_find_thread_points_by_tid (TIDGET (inferior_ptid), 0);
}
}
- *addr_p = (CORE_ADDR) (uintptr_t) siginfo_p->si_addr;
+ *addr_p = (CORE_ADDR) (uintptr_t) siginfo.si_addr;
return 1;
}
return start <= addr + mask && start + length - 1 >= addr;
}
+/* Return the number of registers needed for a masked hardware watchpoint. */
+
+static int
+ppc_linux_masked_watch_num_registers (struct target_ops *target,
+ CORE_ADDR addr, CORE_ADDR mask)
+{
+ if (!have_ptrace_booke_interface ()
+ || (booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0)
+ return -1;
+ else if ((mask & 0xC0000000) != 0xC0000000)
+ {
+ warning (_("The given mask covers kernel address space "
+ "and cannot be used.\n"));
+
+ return -2;
+ }
+ else
+ return 2;
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
{
int sizeof_auxv_field = ppc_linux_target_wordsize ();
- enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
gdb_byte *ptr = *readptr;
if (endptr == ptr)
t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint;
t->to_insert_watchpoint = ppc_linux_insert_watchpoint;
t->to_remove_watchpoint = ppc_linux_remove_watchpoint;
+ t->to_insert_mask_watchpoint = ppc_linux_insert_mask_watchpoint;
+ t->to_remove_mask_watchpoint = ppc_linux_remove_mask_watchpoint;
t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint;
t->to_stopped_data_address = ppc_linux_stopped_data_address;
t->to_watchpoint_addr_within_range = ppc_linux_watchpoint_addr_within_range;
t->to_can_accel_watchpoint_condition
= ppc_linux_can_accel_watchpoint_condition;
+ t->to_masked_watch_num_registers = ppc_linux_masked_watch_num_registers;
+ t->to_ranged_break_num_registers = ppc_linux_ranged_break_num_registers;
t->to_read_description = ppc_linux_read_description;
t->to_auxv_parse = ppc_linux_auxv_parse;