[v4,10/12,PowerPC] Add support for EBB and PMU registers

Message ID 20180815000608.26840-11-pedromfc@linux.ibm.com
State New, archived
Headers

Commit Message

Pedro Franco de Carvalho Aug. 15, 2018, 12:06 a.m. UTC
  From: Edjunior Barbosa Machado <emachado@linux.vnet.ibm.com>

This patch adds support for registers of the Event Based Branching and
Performance Monitoring Units for the powerpc linux native and core
file targets, and for the powerpc linux server stub.

All three EBB registers are accessible.  Only a subset of the PMU
registers can be accessed through ptrace.  Because of this, the PMU
registers are enumerated individually in gdbarch_tdep, as opposed to
having a single "have_pmu" flag.  This is intended to make it easier
to add additional PMU registers in the future, since checking a
"have_pmu" flag elsewhere in the code would no longer be correct.  The
tdesc feature is named org.gnu.gdb.power.linux.pmu because of this.

It's unclear if it makes sense to save and restore these registers
across function calls, since some of them can be modified
asynchronously.  They are also not tracked in record-replay mode.

The kernel can return ENODATA when ptrace is used to get the EBB
registers, unless a linux performance event that uses EBB is open in
the inferior.  For this reason, the "fill" functions in the server
stub for the ebb register sets is not implemented.

Since gdbserver writes all registers in one go before resuming the
inferior, this error would not be detected at the time the user tries
to write to one of the registers on the client side, and gdbserver
would print out warnings every time it resumes the inferior when no
ebb performance event is opened, so there is currently no
straightforward way to handle this case.  This means the ebb registers
in the client-side regcache can become dirty when the user tries to
write to them, until the inferior is resumed and stopped again.

Another limitation for the ebb registers is that traceframes don't
record if registers are available or not, so if these registers are
collected when a tracepoint is hit and the inferior has no ebb event
opened, the user will see zero values for all of them, instead of the
usual <unavailable>.

YYYY-MM-DD  Edjunior Barbosa Machado  <emachado@linux.vnet.ibm.com>
	    Pedro Franco de Carvalho  <pedromfc@linux.ibm.com>

	* arch/ppc-linux-common.h (PPC_LINUX_SIZEOF_EBBREGSET)
	(PPC_LINUX_SIZEOF_PMUREGSET): Declare.
	* nat/ppc-linux.h (PPC_FEATURE2_EBB, NT_PPC_EBB, NT_PPC_PMU):
	Define if not already defined.
	* features/rs6000/power-ebb.xml: New file.
	* features/rs6000/power-linux-pmu.xml: New file.
	* features/rs6000/powerpc-isa207-vsx32l.xml: Include ebb and pmu
	features.
	* features/rs6000/powerpc-isa207-vsx64l.xml: Likewise.
	* features/rs6000/powerpc-isa207-vsx32l.c: Re-generate.
	* features/rs6000/powerpc-isa207-vsx64l.c: Re-generate.
	* regformats/rs6000/powerpc-isa207-vsx32l.dat: Re-generate.
	* regformats/rs6000/powerpc-isa207-vsx64l.dat: Re-generate.
	* ppc-linux-nat.c (fetch_register, fetch_ppc_registers): Call
	fetch_regset with ebb and pmu regsets.
	(ppc_linux_regset_available_p): New function.
	(store_register, store_ppc_registers): Call store_regset with ebb
	and pmu regsets.
	(ppc_linux_nat_target::read_description): Set isa207 field in the
	features struct if ebb and pmu are avaiable.
	* ppc-linux-tdep.c (ppc32_regmap_ebb, ppc32_regmap_pmu)
	(ppc32_linux_ebbregset, ppc32_linux_pmuregset): New globals.
	(ppc_linux_iterate_over_regset_sections): Call back with the ebb
	and pmu regsets.
	(ppc_linux_core_read_description): Check if the pmu section is
	present and set isa207 in the features struct.
	* ppc-linux-tdep.h (ppc32_linux_ebbregset)
	(ppc32_linux_pmuregset): Declare.
	* ppc-tdep.h (struct gdbarch_tdep) <ppc_mmcr0_regnum>: New field.
	<ppc_mmcr2_regnum, ppc_siar_regnum, ppc_sdar_regnum>: New fields.
	<ppc_sier_regnum>: New field.
	(enum): <PPC_BESCR_REGNUM, PPC_EBBHR_REGNUM, PPC_EBBRR_REGNUM>:
	New enum values.
	<PPC_MMCR0_REGNUM, PPC_MMCR2_REGNUM, PPC_SIAR_REGNUM>: New enum
	values.
	<PPC_SDAR_REGNUM, PPC_SIER_REGNUM>: New enum values.
	(PPC_IS_EBB_REGNUM, PPC_IS_PMU_REGNUM): Define.
	* rs6000-tdep.c (rs6000_gdbarch_init): Look for and validate the
	ebb and pmu features.

gdb/gdbserver/ChangeLog:
YYYY-MM-DD  Pedro Franco de Carvalho  <pedromfc@linux.ibm.com>

	* configure.srv (powerpc*-*-linux*): Add rs6000/power-ebb.xml and
	rs6000/power-linux-pmu.xml to srv_xmlfiles.
	* linux-ppc-low.c (ppc_store_ebbregset, ppc_fill_pmuregset)
	(ppc_store_pmuregset): New functions.
	(ppc_regsets): Add entries for ebb and pmu regsets.
	(ppc_arch_setup): Set isa207 in features struct if the ebb and
	pmu regsets are available.  Set sizes for these regsets.

gdb/doc/ChangeLog:
YYYY-MM-DD  Pedro Franco de Carvalho  <pedromfc@linux.ibm.com>

	* gdb.texinfo (PowerPC Features): Describe new features
	"org.gnu.gdb.power.ebb" and "org.gnu.gdb.power.linux.pmu".
---
 gdb/arch/ppc-linux-common.h                     |   2 +
 gdb/doc/gdb.texinfo                             |   9 ++
 gdb/features/rs6000/power-ebb.xml               |  14 ++++
 gdb/features/rs6000/power-linux-pmu.xml         |  17 ++++
 gdb/features/rs6000/powerpc-isa207-vsx32l.c     |  12 +++
 gdb/features/rs6000/powerpc-isa207-vsx32l.xml   |   2 +
 gdb/features/rs6000/powerpc-isa207-vsx64l.c     |  12 +++
 gdb/features/rs6000/powerpc-isa207-vsx64l.xml   |   2 +
 gdb/gdbserver/configure.srv                     |   2 +
 gdb/gdbserver/linux-ppc-low.c                   |  57 ++++++++++++-
 gdb/nat/ppc-linux.h                             |  13 +++
 gdb/ppc-linux-nat.c                             | 105 +++++++++++++++++++++++-
 gdb/ppc-linux-tdep.c                            |  54 +++++++++++-
 gdb/ppc-linux-tdep.h                            |   2 +
 gdb/ppc-tdep.h                                  |  28 +++++++
 gdb/regformats/rs6000/powerpc-isa207-vsx32l.dat |   8 ++
 gdb/regformats/rs6000/powerpc-isa207-vsx64l.dat |   8 ++
 gdb/rs6000-tdep.c                               |  74 ++++++++++++++++-
 18 files changed, 417 insertions(+), 4 deletions(-)
 create mode 100644 gdb/features/rs6000/power-ebb.xml
 create mode 100644 gdb/features/rs6000/power-linux-pmu.xml
  

Comments

Pedro Alves Aug. 16, 2018, 4:51 p.m. UTC | #1
On 08/15/2018 01:06 AM, Pedro Franco de Carvalho wrote:
> From: Edjunior Barbosa Machado <emachado@linux.vnet.ibm.com>
> 
> This patch adds support for registers of the Event Based Branching and
> Performance Monitoring Units for the powerpc linux native and core
> file targets, and for the powerpc linux server stub.
> 
> All three EBB registers are accessible.  Only a subset of the PMU
> registers can be accessed through ptrace.  Because of this, the PMU
> registers are enumerated individually in gdbarch_tdep, as opposed to
> having a single "have_pmu" flag.  This is intended to make it easier
> to add additional PMU registers in the future, since checking a
> "have_pmu" flag elsewhere in the code would no longer be correct.  The
> tdesc feature is named org.gnu.gdb.power.linux.pmu because of this.
> 
> It's unclear if it makes sense to save and restore these registers
> across function calls, since some of them can be modified
> asynchronously.  They are also not tracked in record-replay mode.
> 
> The kernel can return ENODATA when ptrace is used to get the EBB
> registers, unless a linux performance event that uses EBB is open in
> the inferior.  For this reason, the "fill" functions in the server
> stub for the ebb register sets is not implemented.
> 
> Since gdbserver writes all registers in one go before resuming the
> inferior, this error would not be detected at the time the user tries
> to write to one of the registers on the client side, and gdbserver
> would print out warnings every time it resumes the inferior when no
> ebb performance event is opened, so there is currently no
> straightforward way to handle this case.  This means the ebb registers
> in the client-side regcache can become dirty when the user tries to
> write to them, until the inferior is resumed and stopped again.

Should we make gdbserver flush the regcache after handling 'G' and 'P'
packets?

> 
> Another limitation for the ebb registers is that traceframes don't
> record if registers are available or not, so if these registers are
> collected when a tracepoint is hit and the inferior has no ebb event
> opened, the user will see zero values for all of them, instead of the
> usual <unavailable>.

That'd be nice to fix somehow, at some point.

Thanks,
Pedro Alves
  
Pedro Franco de Carvalho Aug. 16, 2018, 6:15 p.m. UTC | #2
Pedro Alves <palves@redhat.com> writes:

>> Since gdbserver writes all registers in one go before resuming the
>> inferior, this error would not be detected at the time the user tries
>> to write to one of the registers on the client side, and gdbserver
>> would print out warnings every time it resumes the inferior when no
>> ebb performance event is opened, so there is currently no
>> straightforward way to handle this case.  This means the ebb registers
>> in the client-side regcache can become dirty when the user tries to
>> write to them, until the inferior is resumed and stopped again.
>
> Should we make gdbserver flush the regcache after handling 'G' and 'P'
> packets?

Yes, I think this would work.  It would have to flush the regcache and
if anything goes wrong reply with an error packet.  This would probably
involve catching exceptions raised by target_ops::store_register in
server.c.

Thanks!

--
Pedro Franco de Carvalho
  

Patch

diff --git a/gdb/arch/ppc-linux-common.h b/gdb/arch/ppc-linux-common.h
index f0e4a4221f..50e9e064d0 100644
--- a/gdb/arch/ppc-linux-common.h
+++ b/gdb/arch/ppc-linux-common.h
@@ -33,6 +33,8 @@  struct target_desc;
 #define PPC_LINUX_SIZEOF_PPRREGSET 8
 #define PPC_LINUX_SIZEOF_DSCRREGSET 8
 #define PPC_LINUX_SIZEOF_TARREGSET 8
+#define PPC_LINUX_SIZEOF_EBBREGSET (3*8)
+#define PPC_LINUX_SIZEOF_PMUREGSET (5*8)
 
 /* Check if the hwcap auxv entry indicates that isa205 is supported.  */
 bool ppc_linux_has_isa205 (CORE_ADDR hwcap);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 97b3308648..f30a459e9e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42791,6 +42791,15 @@  contain the 64-bit register @samp{dscr}.
 The @samp{org.gnu.gdb.power.tar} feature is optional.  It should
 contain the 64-bit register @samp{tar}.
 
+The @samp{org.gnu.gdb.power.ebb} feature is optional.  It should
+contain registers @samp{bescr}, @samp{ebbhr} and @samp{ebbrr}, all
+64-bit wide.
+
+The @samp{org.gnu.gdb.power.linux.pmu} feature is optional.  It should
+contain registers @samp{mmcr0}, @samp{mmcr2}, @samp{siar}, @samp{sdar}
+and @samp{sier}, all 64-bit wide.  This is the subset of the isa 2.07
+server PMU registers provided by @sc{gnu}/Linux.
+
 @node S/390 and System z Features
 @subsection S/390 and System z Features
 @cindex target descriptions, S/390 features
diff --git a/gdb/features/rs6000/power-ebb.xml b/gdb/features/rs6000/power-ebb.xml
new file mode 100644
index 0000000000..7bec21f916
--- /dev/null
+++ b/gdb/features/rs6000/power-ebb.xml
@@ -0,0 +1,14 @@ 
+<?xml version="1.0"?>
+<!-- Copyright (C) 2018 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- POWER8 Event-based Branching Registers.  -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.ebb">
+  <reg name="bescr" bitsize="64" type="uint64" save-restore="no"/>
+  <reg name="ebbhr" bitsize="64" type="uint64" save-restore="no"/>
+  <reg name="ebbrr" bitsize="64" type="uint64" save-restore="no"/>
+</feature>
diff --git a/gdb/features/rs6000/power-linux-pmu.xml b/gdb/features/rs6000/power-linux-pmu.xml
new file mode 100644
index 0000000000..c60b44462d
--- /dev/null
+++ b/gdb/features/rs6000/power-linux-pmu.xml
@@ -0,0 +1,17 @@ 
+<?xml version="1.0"?>
+<!-- Copyright (C) 2018 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- Subset of the POWER8 ISA 2.07 Performance Monitor Registers
+     provided by Linux.  -->
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.power.linux.pmu">
+  <reg name="mmcr0" bitsize="64" type="uint64" save-restore="no"/>
+  <reg name="mmcr2" bitsize="64" type="uint64" save-restore="no"/>
+  <reg name="siar" bitsize="64" type="uint64" save-restore="no"/>
+  <reg name="sdar" bitsize="64" type="uint64" save-restore="no"/>
+  <reg name="sier" bitsize="64" type="uint64" save-restore="no"/>
+</feature>
diff --git a/gdb/features/rs6000/powerpc-isa207-vsx32l.c b/gdb/features/rs6000/powerpc-isa207-vsx32l.c
index 73c7a01077..8fe0f5e438 100644
--- a/gdb/features/rs6000/powerpc-isa207-vsx32l.c
+++ b/gdb/features/rs6000/powerpc-isa207-vsx32l.c
@@ -199,5 +199,17 @@  initialize_tdesc_powerpc_isa207_vsx32l (void)
   feature = tdesc_create_feature (result, "org.gnu.gdb.power.tar");
   tdesc_create_reg (feature, "tar", 141, 1, NULL, 64, "uint64");
 
+  feature = tdesc_create_feature (result, "org.gnu.gdb.power.ebb");
+  tdesc_create_reg (feature, "bescr", 142, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "ebbhr", 143, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "ebbrr", 144, 0, NULL, 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.power.linux.pmu");
+  tdesc_create_reg (feature, "mmcr0", 145, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "mmcr2", 146, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "siar", 147, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "sdar", 148, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "sier", 149, 0, NULL, 64, "uint64");
+
   tdesc_powerpc_isa207_vsx32l = result;
 }
diff --git a/gdb/features/rs6000/powerpc-isa207-vsx32l.xml b/gdb/features/rs6000/powerpc-isa207-vsx32l.xml
index 599e6a24a2..635eef52ee 100644
--- a/gdb/features/rs6000/powerpc-isa207-vsx32l.xml
+++ b/gdb/features/rs6000/powerpc-isa207-vsx32l.xml
@@ -16,4 +16,6 @@ 
   <xi:include href="power-ppr.xml"/>
   <xi:include href="power-dscr.xml"/>
   <xi:include href="power-tar.xml"/>
+  <xi:include href="power-ebb.xml"/>
+  <xi:include href="power-linux-pmu.xml"/>
 </target>
diff --git a/gdb/features/rs6000/powerpc-isa207-vsx64l.c b/gdb/features/rs6000/powerpc-isa207-vsx64l.c
index 9c735522d2..0af1879e10 100644
--- a/gdb/features/rs6000/powerpc-isa207-vsx64l.c
+++ b/gdb/features/rs6000/powerpc-isa207-vsx64l.c
@@ -199,5 +199,17 @@  initialize_tdesc_powerpc_isa207_vsx64l (void)
   feature = tdesc_create_feature (result, "org.gnu.gdb.power.tar");
   tdesc_create_reg (feature, "tar", 141, 1, NULL, 64, "uint64");
 
+  feature = tdesc_create_feature (result, "org.gnu.gdb.power.ebb");
+  tdesc_create_reg (feature, "bescr", 142, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "ebbhr", 143, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "ebbrr", 144, 0, NULL, 64, "uint64");
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.power.linux.pmu");
+  tdesc_create_reg (feature, "mmcr0", 145, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "mmcr2", 146, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "siar", 147, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "sdar", 148, 0, NULL, 64, "uint64");
+  tdesc_create_reg (feature, "sier", 149, 0, NULL, 64, "uint64");
+
   tdesc_powerpc_isa207_vsx64l = result;
 }
diff --git a/gdb/features/rs6000/powerpc-isa207-vsx64l.xml b/gdb/features/rs6000/powerpc-isa207-vsx64l.xml
index 42a23cc001..67dff71fe2 100644
--- a/gdb/features/rs6000/powerpc-isa207-vsx64l.xml
+++ b/gdb/features/rs6000/powerpc-isa207-vsx64l.xml
@@ -16,4 +16,6 @@ 
   <xi:include href="power-ppr.xml"/>
   <xi:include href="power-dscr.xml"/>
   <xi:include href="power-tar.xml"/>
+  <xi:include href="power-ebb.xml"/>
+  <xi:include href="power-linux-pmu.xml"/>
 </target>
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 0cb50627aa..a18d7b6357 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -249,6 +249,8 @@  case "${target}" in
 			srv_xmlfiles="${srv_xmlfiles} rs6000/power-dscr.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/power-ppr.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/power-tar.xml"
+			srv_xmlfiles="${srv_xmlfiles} rs6000/power-ebb.xml"
+			srv_xmlfiles="${srv_xmlfiles} rs6000/power-linux-pmu.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-e500l.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/power-spe.xml"
 			srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-64l.xml"
diff --git a/gdb/gdbserver/linux-ppc-low.c b/gdb/gdbserver/linux-ppc-low.c
index c86bfd5577..261247861c 100644
--- a/gdb/gdbserver/linux-ppc-low.c
+++ b/gdb/gdbserver/linux-ppc-low.c
@@ -526,6 +526,44 @@  ppc_store_tarregset (struct regcache *regcache, const void *buf)
 }
 
 static void
+ppc_store_ebbregset (struct regcache *regcache, const void *buf)
+{
+  const char *regset = (const char *) buf;
+
+  /* The order in the kernel regset is: EBBRR, EBBHR, BESCR.  In the
+     .dat file is BESCR, EBBHR, EBBRR.  */
+  supply_register_by_name (regcache, "ebbrr", &regset[0]);
+  supply_register_by_name (regcache, "ebbhr", &regset[8]);
+  supply_register_by_name (regcache, "bescr", &regset[16]);
+}
+
+static void
+ppc_fill_pmuregset (struct regcache *regcache, void *buf)
+{
+  char *regset = (char *) buf;
+
+  /* The order in the kernel regset is SIAR, SDAR, SIER, MMCR2, MMCR0.
+     In the .dat file is MMCR0, MMCR2, SIAR, SDAR, SIER.  */
+  collect_register_by_name (regcache, "siar", &regset[0]);
+  collect_register_by_name (regcache, "sdar", &regset[8]);
+  collect_register_by_name (regcache, "sier", &regset[16]);
+  collect_register_by_name (regcache, "mmcr2", &regset[24]);
+  collect_register_by_name (regcache, "mmcr0", &regset[32]);
+}
+
+static void
+ppc_store_pmuregset (struct regcache *regcache, const void *buf)
+{
+  const char *regset = (const char *) buf;
+
+  supply_register_by_name (regcache, "siar", &regset[0]);
+  supply_register_by_name (regcache, "sdar", &regset[8]);
+  supply_register_by_name (regcache, "sier", &regset[16]);
+  supply_register_by_name (regcache, "mmcr2", &regset[24]);
+  supply_register_by_name (regcache, "mmcr0", &regset[32]);
+}
+
+static void
 ppc_fill_vsxregset (struct regcache *regcache, void *buf)
 {
   int i, base;
@@ -634,6 +672,10 @@  static struct regset_info ppc_regsets[] = {
      fetch them every time, but still fall back to PTRACE_PEEKUSER for the
      general registers.  Some kernels support these, but not the newer
      PPC_PTRACE_GETREGS.  */
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_EBB, 0, EXTENDED_REGS,
+    NULL, ppc_store_ebbregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PMU, 0, EXTENDED_REGS,
+    ppc_fill_pmuregset, ppc_store_pmuregset },
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_TAR, 0, EXTENDED_REGS,
     ppc_fill_tarregset, ppc_store_tarregset },
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PPC_PPR, 0, EXTENDED_REGS,
@@ -714,8 +756,13 @@  ppc_arch_setup (void)
       features.ppr_dscr = true;
       if ((ppc_hwcap2 & PPC_FEATURE2_ARCH_2_07)
 	  && (ppc_hwcap2 & PPC_FEATURE2_TAR)
+	  && (ppc_hwcap2 & PPC_FEATURE2_EBB)
 	  && ppc_check_regset (tid, NT_PPC_TAR,
-			       PPC_LINUX_SIZEOF_TARREGSET))
+			       PPC_LINUX_SIZEOF_TARREGSET)
+	  && ppc_check_regset (tid, NT_PPC_EBB,
+			       PPC_LINUX_SIZEOF_EBBREGSET)
+	  && ppc_check_regset (tid, NT_PPC_PMU,
+			       PPC_LINUX_SIZEOF_PMUREGSET))
 	features.isa207 = true;
     }
 
@@ -778,6 +825,14 @@  ppc_arch_setup (void)
 	    regset->size = (features.isa207 ?
 			    PPC_LINUX_SIZEOF_TARREGSET : 0);
 	    break;
+	  case NT_PPC_EBB:
+	    regset->size = (features.isa207 ?
+			    PPC_LINUX_SIZEOF_EBBREGSET : 0);
+	    break;
+	  case NT_PPC_PMU:
+	    regset->size = (features.isa207 ?
+			    PPC_LINUX_SIZEOF_PMUREGSET : 0);
+	    break;
 	  default:
 	    break;
 	  }
diff --git a/gdb/nat/ppc-linux.h b/gdb/nat/ppc-linux.h
index 913a9e0bd8..c965030d91 100644
--- a/gdb/nat/ppc-linux.h
+++ b/gdb/nat/ppc-linux.h
@@ -60,6 +60,9 @@ 
 #ifndef PPC_FEATURE2_TAR
 #define PPC_FEATURE2_TAR 0x04000000
 #endif
+#ifndef PPC_FEATURE2_EBB
+#define PPC_FEATURE2_EBB 0x10000000
+#endif
 
 /* Glibc's headers don't define PTRACE_GETVRREGS so we cannot use a
    configure time check.  Some older glibc's (for instance 2.2.1)
@@ -106,6 +109,16 @@ 
 #define NT_PPC_DSCR 0x105
 #endif
 
+/* Event Based Branch Registers.  */
+#ifndef NT_PPC_EBB
+#define NT_PPC_EBB 0x106
+#endif
+
+/* Performance Monitor Registers.  */
+#ifndef NT_PPC_PMU
+#define NT_PPC_PMU 0x107
+#endif
+
 /* Return the wordsize of the target, either 4 or 8 bytes.  */
 int ppc_linux_target_wordsize (int tid);
 
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index ffe1f813d2..3466f4dc7f 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -666,6 +666,24 @@  fetch_register (struct regcache *regcache, int tid, int regno)
 		    &ppc32_linux_tarregset);
       return;
     }
+  else if (PPC_IS_EBB_REGNUM (regno))
+    {
+      gdb_assert (tdep->have_ebb);
+
+      fetch_regset (regcache, tid, NT_PPC_EBB,
+		    PPC_LINUX_SIZEOF_EBBREGSET,
+		    &ppc32_linux_ebbregset);
+      return;
+    }
+  else if (PPC_IS_PMU_REGNUM (regno))
+    {
+      gdb_assert (tdep->ppc_mmcr0_regnum != -1);
+
+      fetch_regset (regcache, tid, NT_PPC_PMU,
+		    PPC_LINUX_SIZEOF_PMUREGSET,
+		    &ppc32_linux_pmuregset);
+      return;
+    }
 
   if (regaddr == -1)
     {
@@ -871,6 +889,14 @@  fetch_ppc_registers (struct regcache *regcache, int tid)
     fetch_regset (regcache, tid, NT_PPC_TAR,
 		  PPC_LINUX_SIZEOF_TARREGSET,
 		  &ppc32_linux_tarregset);
+  if (tdep->have_ebb)
+    fetch_regset (regcache, tid, NT_PPC_EBB,
+		  PPC_LINUX_SIZEOF_EBBREGSET,
+		  &ppc32_linux_ebbregset);
+  if (tdep->ppc_mmcr0_regnum != -1)
+    fetch_regset (regcache, tid, NT_PPC_PMU,
+		  PPC_LINUX_SIZEOF_PMUREGSET,
+		  &ppc32_linux_pmuregset);
 }
 
 /* Fetch registers from the child process.  Fetch all registers if
@@ -1078,6 +1104,24 @@  store_register (const struct regcache *regcache, int tid, int regno)
 		    &ppc32_linux_tarregset);
       return;
     }
+  else if (PPC_IS_EBB_REGNUM (regno))
+    {
+      gdb_assert (tdep->have_ebb);
+
+      store_regset (regcache, tid, regno, NT_PPC_EBB,
+		    PPC_LINUX_SIZEOF_EBBREGSET,
+		    &ppc32_linux_ebbregset);
+      return;
+    }
+  else if (PPC_IS_PMU_REGNUM (regno))
+    {
+      gdb_assert (tdep->ppc_mmcr0_regnum != -1);
+
+      store_regset (regcache, tid, regno, NT_PPC_PMU,
+		    PPC_LINUX_SIZEOF_PMUREGSET,
+		    &ppc32_linux_pmuregset);
+      return;
+    }
 
   if (regaddr == -1)
     return;
@@ -1252,6 +1296,50 @@  store_fp_regs (const struct regcache *regcache, int tid, int regno)
     store_register (regcache, tid, tdep->ppc_fp0_regnum + i);
 }
 
+/* Returns true if all the registers in REGSET are valid in REGCACHE,
+   false if no registers are valid, and throws an internal error if
+   only some are valid.  The regmap field in REGSET must be of type
+   regcache_map_entry.  */
+
+static bool
+ppc_linux_regset_available_p (const struct regcache *regcache,
+			      const struct regset *regset)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  const struct regcache_map_entry *map;
+  int count;
+  int valid = 0;
+  int total = 0;
+
+  for (map = (const struct regcache_map_entry *) regset->regmap;
+       (count = map->count) != 0;
+       map++)
+    {
+      int regno = map->regno;
+
+      /* Ignore REGCACHE_MAP_SKIP.  */
+      if (regno >= 0 && regno < gdbarch_num_regs (gdbarch))
+	{
+	  for (int i = regno; i < regno + count; i++)
+	    {
+	      if (regcache->get_register_status (i) == REG_VALID)
+		valid++;
+	      total++;
+	    }
+	}
+    }
+
+  if (valid > 0 && valid < total)
+    internal_error (__FILE__, __LINE__,
+		    _("Only some registers in a regset"
+		      "are valid."));
+
+  if (valid == total)
+    return true;
+  else
+    return false;
+}
+
 static void
 store_ppc_registers (const struct regcache *regcache, int tid)
 {
@@ -1301,6 +1389,18 @@  store_ppc_registers (const struct regcache *regcache, int tid)
     store_regset (regcache, tid, -1, NT_PPC_TAR,
 		  PPC_LINUX_SIZEOF_TARREGSET,
 		  &ppc32_linux_tarregset);
+
+  /* EBB registers can be unavailable.  */
+  if (tdep->have_ebb
+      && ppc_linux_regset_available_p (regcache, &ppc32_linux_ebbregset))
+    store_regset (regcache, tid, -1, NT_PPC_EBB,
+		  PPC_LINUX_SIZEOF_EBBREGSET,
+		  &ppc32_linux_ebbregset);
+
+  if (tdep->ppc_mmcr0_regnum != -1)
+    store_regset (regcache, tid, -1, NT_PPC_PMU,
+		  PPC_LINUX_SIZEOF_PMUREGSET,
+		  &ppc32_linux_pmuregset);
 }
 
 /* Fetch the AT_HWCAP entry from the aux vector.  */
@@ -2429,7 +2529,10 @@  ppc_linux_nat_target::read_description ()
       features.ppr_dscr = true;
       if ((hwcap2 & PPC_FEATURE2_ARCH_2_07)
 	  && (hwcap2 & PPC_FEATURE2_TAR)
-	  && check_regset (tid, NT_PPC_TAR, PPC_LINUX_SIZEOF_TARREGSET))
+	  && (hwcap2 & PPC_FEATURE2_EBB)
+	  && check_regset (tid, NT_PPC_TAR, PPC_LINUX_SIZEOF_TARREGSET)
+	  && check_regset (tid, NT_PPC_EBB, PPC_LINUX_SIZEOF_EBBREGSET)
+	  && check_regset (tid, NT_PPC_PMU, PPC_LINUX_SIZEOF_PMUREGSET))
 	features.isa207 = true;
     }
 
diff --git a/gdb/ppc-linux-tdep.c b/gdb/ppc-linux-tdep.c
index bba6ba7684..503357299e 100644
--- a/gdb/ppc-linux-tdep.c
+++ b/gdb/ppc-linux-tdep.c
@@ -507,6 +507,24 @@  static const struct regcache_map_entry ppc32_regmap_tar[] =
       { 0 }
   };
 
+static const struct regcache_map_entry ppc32_regmap_ebb[] =
+  {
+      { 1, PPC_EBBRR_REGNUM, 8 },
+      { 1, PPC_EBBHR_REGNUM, 8 },
+      { 1, PPC_BESCR_REGNUM, 8 },
+      { 0 }
+  };
+
+static const struct regcache_map_entry ppc32_regmap_pmu[] =
+  {
+      { 1, PPC_SIAR_REGNUM, 8 },
+      { 1, PPC_SDAR_REGNUM, 8 },
+      { 1, PPC_SIER_REGNUM, 8 },
+      { 1, PPC_MMCR2_REGNUM, 8 },
+      { 1, PPC_MMCR0_REGNUM, 8 },
+      { 0 }
+  };
+
 static const struct regset ppc32_linux_gregset = {
   &ppc32_linux_reg_offsets,
   ppc_linux_supply_gregset,
@@ -587,6 +605,18 @@  const struct regset ppc32_linux_tarregset = {
   regcache_collect_regset
 };
 
+const struct regset ppc32_linux_ebbregset = {
+  ppc32_regmap_ebb,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
+const struct regset ppc32_linux_pmuregset = {
+  ppc32_regmap_pmu,
+  regcache_supply_regset,
+  regcache_collect_regset
+};
+
 const struct regset *
 ppc_linux_gregset (int wordsize)
 {
@@ -662,6 +692,22 @@  ppc_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
     cb (".reg-ppc-tar", PPC_LINUX_SIZEOF_TARREGSET,
 	PPC_LINUX_SIZEOF_TARREGSET,
 	&ppc32_linux_tarregset, "Target Address Register", cb_data);
+
+  /* EBB registers are unavailable when ptrace returns ENODATA.  Check
+     availability when generating a core file (regcache != NULL).  */
+  if (tdep->have_ebb)
+    if (regcache == NULL
+	|| REG_VALID == regcache->get_register_status (PPC_BESCR_REGNUM))
+      cb (".reg-ppc-ebb", PPC_LINUX_SIZEOF_EBBREGSET,
+	  PPC_LINUX_SIZEOF_EBBREGSET,
+	  &ppc32_linux_ebbregset, "Event-based Branching Registers",
+	  cb_data);
+
+  if (tdep->ppc_mmcr0_regnum != -1)
+    cb (".reg-ppc-pmu", PPC_LINUX_SIZEOF_PMUREGSET,
+	PPC_LINUX_SIZEOF_PMUREGSET,
+	&ppc32_linux_pmuregset, "Performance Monitor Registers",
+	cb_data);
 }
 
 static void
@@ -1077,6 +1123,7 @@  ppc_linux_core_read_description (struct gdbarch *gdbarch,
   asection *ppr = bfd_get_section_by_name (abfd, ".reg-ppc-ppr");
   asection *dscr = bfd_get_section_by_name (abfd, ".reg-ppc-dscr");
   asection *tar = bfd_get_section_by_name (abfd, ".reg-ppc-tar");
+  asection *pmu = bfd_get_section_by_name (abfd, ".reg-ppc-pmu");
 
   if (! section)
     return NULL;
@@ -1112,7 +1159,12 @@  ppc_linux_core_read_description (struct gdbarch *gdbarch,
   if (ppr && dscr)
     {
       features.ppr_dscr = true;
-      if (tar)
+
+      /* We don't require the EBB note section to be present in the
+	 core file to select isa207 because these registers could have
+	 been unavailable when the core file was created.  They will
+	 be in the tdep but will show as unavailable.  */
+      if (tar && pmu)
 	features.isa207 = true;
     }
 
diff --git a/gdb/ppc-linux-tdep.h b/gdb/ppc-linux-tdep.h
index d6ddf69354..cce1ddd1ef 100644
--- a/gdb/ppc-linux-tdep.h
+++ b/gdb/ppc-linux-tdep.h
@@ -48,5 +48,7 @@  int ppc_linux_trap_reg_p (struct gdbarch *gdbarch);
 extern const struct regset ppc32_linux_pprregset;
 extern const struct regset ppc32_linux_dscrregset;
 extern const struct regset ppc32_linux_tarregset;
+extern const struct regset ppc32_linux_ebbregset;
+extern const struct regset ppc32_linux_pmuregset;
 
 #endif /* PPC_LINUX_TDEP_H */
diff --git a/gdb/ppc-tdep.h b/gdb/ppc-tdep.h
index e2b968177e..cfe32c9e87 100644
--- a/gdb/ppc-tdep.h
+++ b/gdb/ppc-tdep.h
@@ -265,6 +265,15 @@  struct gdbarch_tdep
     /* Decimal 128 registers.  */
     int ppc_dl0_regnum;		/* First Decimal128 argument register pair.  */
 
+    int have_ebb;
+
+    /* PMU registers.  */
+    int ppc_mmcr0_regnum;
+    int ppc_mmcr2_regnum;
+    int ppc_siar_regnum;
+    int ppc_sdar_regnum;
+    int ppc_sier_regnum;
+
     /* Offset to ABI specific location where link register is saved.  */
     int lr_frame_offset;	
 
@@ -321,12 +330,31 @@  enum {
   PPC_PPR_REGNUM = 172,
   PPC_DSCR_REGNUM = 173,
   PPC_TAR_REGNUM = 174,
+
+  /* EBB registers.  */
+  PPC_BESCR_REGNUM = 175,
+  PPC_EBBHR_REGNUM = 176,
+  PPC_EBBRR_REGNUM = 177,
+
+  /* PMU registers.  */
+  PPC_MMCR0_REGNUM = 178,
+  PPC_MMCR2_REGNUM = 179,
+  PPC_SIAR_REGNUM = 180,
+  PPC_SDAR_REGNUM = 181,
+  PPC_SIER_REGNUM = 182,
+
   PPC_NUM_REGS
 };
 
 /* Big enough to hold the size of the largest register in bytes.  */
 #define PPC_MAX_REGISTER_SIZE	64
 
+#define PPC_IS_EBB_REGNUM(i) \
+	((i) >= PPC_BESCR_REGNUM && (i) <= PPC_EBBRR_REGNUM)
+
+#define PPC_IS_PMU_REGNUM(i) \
+	((i) >= PPC_MMCR0_REGNUM && (i) <= PPC_SIER_REGNUM)
+
 /* An instruction to match.  */
 
 struct ppc_insn_pattern
diff --git a/gdb/regformats/rs6000/powerpc-isa207-vsx32l.dat b/gdb/regformats/rs6000/powerpc-isa207-vsx32l.dat
index 2b6e0a9c8d..0718d72d93 100644
--- a/gdb/regformats/rs6000/powerpc-isa207-vsx32l.dat
+++ b/gdb/regformats/rs6000/powerpc-isa207-vsx32l.dat
@@ -145,3 +145,11 @@  expedite:r1,pc
 64:ppr
 64:dscr
 64:tar
+64:bescr
+64:ebbhr
+64:ebbrr
+64:mmcr0
+64:mmcr2
+64:siar
+64:sdar
+64:sier
diff --git a/gdb/regformats/rs6000/powerpc-isa207-vsx64l.dat b/gdb/regformats/rs6000/powerpc-isa207-vsx64l.dat
index 095bd7f2d3..510c6c87da 100644
--- a/gdb/regformats/rs6000/powerpc-isa207-vsx64l.dat
+++ b/gdb/regformats/rs6000/powerpc-isa207-vsx64l.dat
@@ -145,3 +145,11 @@  expedite:r1,pc
 64:ppr
 64:dscr
 64:tar
+64:bescr
+64:ebbhr
+64:ebbrr
+64:mmcr0
+64:mmcr2
+64:siar
+64:sdar
+64:sier
diff --git a/gdb/rs6000-tdep.c b/gdb/rs6000-tdep.c
index 32bbf45841..a4998c1a39 100644
--- a/gdb/rs6000-tdep.c
+++ b/gdb/rs6000-tdep.c
@@ -5819,7 +5819,7 @@  rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   enum powerpc_elf_abi elf_abi = POWERPC_ELF_AUTO;
   int have_fpu = 0, have_spe = 0, have_mq = 0, have_altivec = 0;
   int have_dfp = 0, have_vsx = 0, have_ppr = 0, have_dscr = 0;
-  int have_tar = 0;
+  int have_tar = 0, have_ebb = 0, have_pmu = 0;
   int tdesc_wordsize = -1;
   const struct target_desc *tdesc = info.target_desc;
   struct tdesc_arch_data *tdesc_data = NULL;
@@ -6159,6 +6159,64 @@  rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 	}
       else
 	have_tar = 0;
+
+      /* Event-based Branching Registers.  */
+      feature = tdesc_find_feature (tdesc,
+				    "org.gnu.gdb.power.ebb");
+      if (feature != NULL)
+	{
+	  static const char *const ebb_regs[] = {
+	    "bescr", "ebbhr", "ebbrr"
+	  };
+
+	  valid_p = 1;
+	  for (i = 0; i < ARRAY_SIZE (ebb_regs); i++)
+	    valid_p &= tdesc_numbered_register (feature, tdesc_data,
+						PPC_BESCR_REGNUM + i,
+						ebb_regs[i]);
+	  if (!valid_p)
+	    {
+	      tdesc_data_cleanup (tdesc_data);
+	      return NULL;
+	    }
+	  have_ebb = 1;
+	}
+      else
+	have_ebb = 0;
+
+      /* Subset of the ISA 2.07 Performance Monitor Registers provided
+	 by Linux.  */
+      feature = tdesc_find_feature (tdesc,
+				    "org.gnu.gdb.power.linux.pmu");
+      if (feature != NULL)
+	{
+	  valid_p = 1;
+
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      PPC_MMCR0_REGNUM,
+					      "mmcr0");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      PPC_MMCR2_REGNUM,
+					      "mmcr2");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      PPC_SIAR_REGNUM,
+					      "siar");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      PPC_SDAR_REGNUM,
+					      "sdar");
+	  valid_p &= tdesc_numbered_register (feature, tdesc_data,
+					      PPC_SIER_REGNUM,
+					      "sier");
+
+	  if (!valid_p)
+	    {
+	      tdesc_data_cleanup (tdesc_data);
+	      return NULL;
+	    }
+	  have_pmu = 1;
+	}
+      else
+	have_pmu = 0;
     }
 
   /* If we have a 64-bit binary on a 32-bit target, complain.  Also
@@ -6356,6 +6414,20 @@  rs6000_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->ppc_ppr_regnum = have_ppr ? PPC_PPR_REGNUM : -1;
   tdep->ppc_dscr_regnum = have_dscr ? PPC_DSCR_REGNUM : -1;
   tdep->ppc_tar_regnum = have_tar ? PPC_TAR_REGNUM : -1;
+  tdep->have_ebb = have_ebb;
+
+  /* If additional pmu registers are added, care must be taken when
+     setting new fields in the tdep below, to maintain compatibility
+     with features that only provide some of the registers.  Currently
+     gdb access to the pmu registers is only supported in linux, and
+     linux only provides a subset of the pmu registers defined in the
+     architecture.  */
+
+  tdep->ppc_mmcr0_regnum = have_pmu ? PPC_MMCR0_REGNUM : -1;
+  tdep->ppc_mmcr2_regnum = have_pmu ? PPC_MMCR2_REGNUM : -1;
+  tdep->ppc_siar_regnum = have_pmu ? PPC_SIAR_REGNUM : -1;
+  tdep->ppc_sdar_regnum = have_pmu ? PPC_SDAR_REGNUM : -1;
+  tdep->ppc_sier_regnum = have_pmu ? PPC_SIER_REGNUM : -1;
 
   set_gdbarch_pc_regnum (gdbarch, PPC_PC_REGNUM);
   set_gdbarch_sp_regnum (gdbarch, PPC_R0_REGNUM + 1);