[PATCH 1/1] Add support for Intel PKRU register to GDB and GDBserver.

Michael Sturm
2015-12-17T15:44:11Z

This patch adds support for the registers added by the
Memory Protection Keys for Userspace (PKU aka PKEYs) feature.
Native and remote debugging are covered by this patch.

The XSAVE area is extended with a new state containing
the 32-bit wide PKRU register.

For more information, please refer to the
Intel(R) 64 and IA-32 Architecures Software Developer's
Manual - Septemper 2015
http://www.intel.com/content/dam/www/public/us/en/documents/
manuals/64-ia-32-architectures-software-developer-manual-325462.pdf

2015-12-08 Michael Sturm <michael.sturm@intel.com>

* amd64-linux-nat.c (amd64_linux_gregset32_reg_offset): Add PKRU register.
* amd64-linux-tdep.c (amd64_linux_gregset_reg_offset): Likewise.
* amd64-linux-tdep.h (AMD64_LINUX_ORIG_RAX_REGNUM): Adjust regnum
calculation.
* amd64-tdep.c (amd64_pkeys_names): New register name for
raw register PKRU.
(amd64_init_abi): Add code to initialize PKRU tdep variables if feature is
present.
* amd64-tdep.h (enum amd64_regnum): Add PKRU register.
* i386-linux.nat.c (GETXSTATEREGS_SUPPLIES): Extend range of
registers supplied via XSTATE by PKRU register.
* i386-linux-tdep.c (i386_linux_gregset_reg_offset): Add PKRU register.
* i386-linux-tdep.h (I386_LINUX_ORIG_EAX_REGNUM): Adjust regnum
calculation.
* i386-tdep.c (i386_pkeys_names): New register name for raw register PKRU.
(i386_pkru_regnum_p): Add function to look up register number of
PKRU raw register.
(i386_register_reggroup_p): Add code to exclude PKRU from general register
group.
(i386_validate_tdesc_p): Handle PKRU feature, add PKRU registers if
feature is present in xcr0.
(i386_gdbarch_init): Adjust number of registers in architecture. Add code
to initialize PKRU feature variables in tdep structure.
* i386-tdep.h (struct gdbarch_tdep): Add feature variables to tdep
structure.
(enum i386_regnum): Add PKRU register.
(i386_PKEYS_NUM_REGS): New define for number of registers in PKRU feature.
(i386_pkru_regnum_p): New prototype.
* i387-tdep.h (xsave_pkeys_offset): New table for PKRU offsets in
XSAVE buffer.
(XSAVE_PKEYS_ADDR): New macro.
(i387_supply_xsave): Change type of clear_bv to unsigned long, add PKRU
register to regclass enum, add code to handle PKRU register.
(i387_collect_xsave): Add PKRU to regclass enunm, add code to handle PKRU
register. Change type of xstate_bv and clear_bv to unsigned long.
* i387-tdep.h (I387_NUM_PKEYS_REGS): New define for number of registers in
PKRU feature.
(I387_PKRU_REGNUM): New macro.
(I387_PKEYSEND_REGNUM): Likewise.
* common/x86-xstate.h: Add PKRU feature bits, mask and XSTATE size.
* features/Makefile: Add PKRU related files.
* features/i386/32bit-pkeys.xml: New file.
* features/i386/64bit-pkeys.xml: Likewise.
* features/i386/amd64-avx512-linux.c: Add PKRU feature.
* features/i386/amd64-avx512-linux.xml: Add 64bit-pkeys.xml.
* features/i386/amd64-avx512.c: Add PKRU feature.
* features/i386/amd64-avx512.xml: Add 64bit-pkeys.xml.
* features/i386/i386-avx512-linux.c: Add PKRU feature.
* features/i386/i386-avx512-linux.xml: Add 32bit-pkeys.xml.
* features/i386/i386-avx512.c: Add PKRU feature.
* features/i386/i386-avx512.xml: Add 32bit-pkeys.xml.
* nat/x86-gcc-cpuid.h (bit_PKU): New bit definition.
(bit_OSPKE): New bit definition.
* regformats/i386/amd64-avx512-linux.dat: Add PKRU register.
* regformats/i386/amd64-avx512.dat: Likewise.
* regformats/i386/i386-avx512-linux.dat: Likewise.
* regformats/i386/i386-avx512-linux.dat: Likewise.
* NEWS: Add

testsuite/
* gdb.arch/i386-pkru.c: New file.
* gdb.arch/i386-pkru.exp: Likewise.

gdbserver/
* configure.srv (srv_i386_32bit_xmlfiles): Add i386/32bit-pkeys.xml.
(srv_i386_64bit_xmlfiles): Add i386/64bit-pkeys.xml.
* i387-fp.c (num_pkeys_registers): New constant for PKRU register.
(struct i387_xsave): Add space for PKRU register.
(i387_cache_to_xsave): Change type of clear_bv to unsigned long. Add code
to handle PKRU register.
( i387_xsave_to_cache): Add code to handle PKRU register.
* linux-x86-low.c (x86_64_regmap): Add PKRU register.

doc/
* gdb.texinfo (i386 Features): Add description of PKRU register.

Change-Id: Ibeda963a9c8123f68e830f59dab4482d170dfab8
Signed-off-by: Michael Sturm <michael.sturm@intel.com>
---
gdb/NEWS | 4 ++
gdb/amd64-linux-nat.c | 1 +
gdb/amd64-linux-tdep.c | 1 +
gdb/amd64-linux-tdep.h | 2 +-
gdb/amd64-tdep.c | 11 ++++
gdb/amd64-tdep.h | 3 +-
gdb/common/x86-xstate.h | 15 +++--
gdb/doc/gdb.texinfo | 4 ++
gdb/features/Makefile | 9 +--
gdb/features/i386/32bit-pkeys.xml | 13 ++++
gdb/features/i386/64bit-pkeys.xml | 13 ++++
gdb/features/i386/amd64-avx512-linux.c | 3 +
gdb/features/i386/amd64-avx512-linux.xml | 1 +
gdb/features/i386/amd64-avx512.c | 3 +
gdb/features/i386/amd64-avx512.xml | 1 +
gdb/features/i386/i386-avx512-linux.c | 3 +
gdb/features/i386/i386-avx512-linux.xml | 1 +
gdb/features/i386/i386-avx512.c | 3 +
gdb/features/i386/i386-avx512.xml | 1 +
gdb/gdbserver/configure.srv | 4 +-
gdb/gdbserver/i387-fp.c | 45 +++++++++++++-
gdb/gdbserver/linux-x86-low.c | 3 +-
gdb/i386-linux-nat.c | 2 +-
gdb/i386-linux-tdep.c | 1 +
gdb/i386-linux-tdep.h | 3 +-
gdb/i386-tdep.c | 53 ++++++++++++++--
gdb/i386-tdep.h | 14 ++++-
gdb/i387-tdep.c | 97 +++++++++++++++++++++++++++---
gdb/i387-tdep.h | 5 ++
gdb/nat/x86-gcc-cpuid.h | 4 ++
gdb/regformats/i386/amd64-avx512-linux.dat | 1 +
gdb/regformats/i386/amd64-avx512.dat | 1 +
gdb/regformats/i386/i386-avx512-linux.dat | 1 +
gdb/regformats/i386/i386-avx512.dat | 1 +
gdb/testsuite/gdb.arch/i386-pkru.c | 97 ++++++++++++++++++++++++++++++
gdb/testsuite/gdb.arch/i386-pkru.exp | 80 ++++++++++++++++++++++++
36 files changed, 474 insertions(+), 30 deletions(-)
create mode 100644 gdb/features/i386/32bit-pkeys.xml
create mode 100644 gdb/features/i386/64bit-pkeys.xml
create mode 100644 gdb/testsuite/gdb.arch/i386-pkru.c
create mode 100644 gdb/testsuite/gdb.arch/i386-pkru.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 484d98d..74103f0 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -150,6 +150,10 @@ show remote exec-event-feature-packet

*** Changes in GDB 7.10

+* GDB now supports access to the PKU register on GNU/Linux. The register is
+ added by the Memory Protection Keys for Userspace feature which will be
+ available in future Intel CPUs.
+
* Support for process record-replay and reverse debugging on aarch64*-linux*
targets has been added. GDB now supports recording of A64 instruction set
including advance SIMD instructions.
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index 397f664..9e6af00 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -66,6 +66,7 @@ static int amd64_linux_gregset32_reg_offset[] =
-1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
-1, -1, -1, -1, -1, -1, -1, -1, /* k0 ... k7 (AVX512) */
-1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm7 (AVX512) */
+ -1, /* PKEYS register PKRU */
ORIG_RAX * 8 /* "orig_eax" */
};
\f
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index ee21635..c3ea81e 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -102,6 +102,7 @@ int amd64_linux_gregset_reg_offset[] =
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
+ -1, /* PKEYS register pkru */
15 * 8 /* "orig_rax" */
};
\f
diff --git a/gdb/amd64-linux-tdep.h b/gdb/amd64-linux-tdep.h
index a850fe1..2a08144 100644
--- a/gdb/amd64-linux-tdep.h
+++ b/gdb/amd64-linux-tdep.h
@@ -26,7 +26,7 @@
/* Register number for the "orig_rax" register. If this register
contains a value >= 0 it is interpreted as the system call number
that the kernel is supposed to restart. */
-#define AMD64_LINUX_ORIG_RAX_REGNUM (AMD64_ZMM31H_REGNUM + 1)
+#define AMD64_LINUX_ORIG_RAX_REGNUM (AMD64_PKRU_REGNUM + 1)

/* Total number of registers for GNU/Linux. */
#define AMD64_LINUX_NUM_REGS (AMD64_LINUX_ORIG_RAX_REGNUM + 1)
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 6096ce9..d0a3c75 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -154,6 +154,10 @@ static const char *amd64_xmm_avx512_names[] = {
"xmm28", "xmm29", "xmm30", "xmm31"
};

+static const char *amd64_pkeys_names[] = {
+ "pkru"
+};
+
/* DWARF Register Number Mapping as defined in the System V psABI,
section 3.6. */

@@ -2987,6 +2991,13 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
tdep->bnd0r_regnum = AMD64_BND0R_REGNUM;
}

+ if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys") != NULL)
+ {
+ tdep->pkeys_register_names = amd64_pkeys_names;
+ tdep->pkru_regnum = AMD64_PKRU_REGNUM;
+ tdep->num_pkeys_regs = 1;
+ }
+
tdep->num_byte_regs = 20;
tdep->num_word_regs = 16;
tdep->num_dword_regs = 16;
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 704225e..969bd89 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -76,7 +76,8 @@ enum amd64_regnum
AMD64_K0_REGNUM,
AMD64_K7_REGNUM = AMD64_K0_REGNUM + 7,
AMD64_ZMM0H_REGNUM,
- AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31
+ AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31,
+ AMD64_PKRU_REGNUM
};

/* Number of general purpose registers. */
diff --git a/gdb/common/x86-xstate.h b/gdb/common/x86-xstate.h
index 9690ccb..bc6b0f1 100644
--- a/gdb/common/x86-xstate.h
+++ b/gdb/common/x86-xstate.h
@@ -35,13 +35,15 @@
#define X86_XSTATE_AVX512 (X86_XSTATE_K | X86_XSTATE_ZMM_H \
| X86_XSTATE_ZMM)

+#define X86_XSTATE_PKRU (1ULL << 9)
+
/* Supported mask and size of the extended state. */
#define X86_XSTATE_X87_MASK X86_XSTATE_X87
#define X86_XSTATE_SSE_MASK (X86_XSTATE_X87 | X86_XSTATE_SSE)
#define X86_XSTATE_AVX_MASK (X86_XSTATE_SSE_MASK | X86_XSTATE_AVX)
#define X86_XSTATE_MPX_MASK (X86_XSTATE_AVX_MASK | X86_XSTATE_MPX)
#define X86_XSTATE_AVX512_MASK (X86_XSTATE_AVX_MASK | X86_XSTATE_AVX512)
-#define X86_XSTATE_MPX_AVX512_MASK (X86_XSTATE_MPX_MASK | X86_XSTATE_AVX512)
+#define X86_XSTATE_MPX_AVX512_MASK (X86_XSTATE_MPX_MASK | X86_XSTATE_AVX512 | X86_XSTATE_PKRU)

#define X86_XSTATE_ALL_MASK (X86_XSTATE_MPX_AVX512_MASK)

@@ -50,18 +52,21 @@
#define X86_XSTATE_BNDREGS_SIZE 1024
#define X86_XSTATE_BNDCFG_SIZE 1088
#define X86_XSTATE_AVX512_SIZE 2688
-#define X86_XSTATE_MAX_SIZE 2688
+#define X86_XSTATE_PKRU_SIZE 2696
+#define X86_XSTATE_MAX_SIZE 2696

/* In case one of the MPX XCR0 bits is set we consider we have MPX. */
#define HAS_MPX(XCR0) (((XCR0) & X86_XSTATE_MPX) != 0)
#define HAS_AVX(XCR0) (((XCR0) & X86_XSTATE_AVX) != 0)
#define HAS_AVX512(XCR0) (((XCR0) & X86_XSTATE_AVX512) != 0)
+#define HAS_PKRU(XCR0) (((XCR0) & X86_XSTATE_PKRU) != 0)

/* Get I386 XSAVE extended state size. */
#define X86_XSTATE_SIZE(XCR0) \
- (HAS_AVX512 (XCR0) ? X86_XSTATE_AVX512_SIZE : \
- (HAS_MPX (XCR0) ? X86_XSTATE_BNDCFG_SIZE : \
- (HAS_AVX (XCR0) ? X86_XSTATE_AVX_SIZE : X86_XSTATE_SSE_SIZE)))
+ (HAS_PKRU (XCR0) ? X86_XSTATE_PKRU_SIZE : \
+ (HAS_AVX512 (XCR0) ? X86_XSTATE_AVX512_SIZE : \
+ (HAS_MPX (XCR0) ? X86_XSTATE_BNDCFG_SIZE : \
+ (HAS_AVX (XCR0) ? X86_XSTATE_AVX_SIZE : X86_XSTATE_SSE_SIZE))))

#endif /* X86_XSTATE_H */
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 7cc1b35..99f4da8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40571,6 +40571,10 @@ describe the additional @sc{zmm} registers:
@samp{zmm16h} through @samp{zmm31h}, only valid for amd64.
@end itemize

+The @samp{org.gnu.gdb.i386.pkeys} feature is optional. It should
+describe a single register, @samp{pkru}. It is a 32-bit register
+valid for i386 and amd64.
+
@node MicroBlaze Features
@subsection MicroBlaze Features
@cindex target descriptions, MicroBlaze features
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 74006e2..3af3ac4 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -272,9 +272,10 @@ $(outdir)/i386/i386-mpx.dat: i386/32bit-core.xml i386/32bit-avx.xml \
$(outdir)/i386/i386-mpx-linux.dat: i386/32bit-core.xml i386/32bit-avx.xml \
i386/32bit-linux.xml i386/32bit-mpx.xml
$(outdir)/i386/i386-avx512.dat: i386/32bit-core.xml i386/32bit-avx.xml \
- i386/32bit-mpx.xml i386/32bit-avx512.xml
+ i386/32bit-mpx.xml i386/32bit-avx512.xml i386/32bit-pkeys.xml
$(outdir)/i386/i386-avx512-linux.dat: i386/32bit-core.xml i386/32bit-avx.xml \
- i386/32bit-linux.xml i386/32bit-mpx.xml i386/32bit-avx512.xml
+ i386/32bit-linux.xml i386/32bit-mpx.xml \
+ i386/32bit-avx512.xml i386/32bit-pkeys.xml
$(outdir)/i386/i386-mmx.dat: i386/32bit-core.xml
$(outdir)/i386/i386-mmx-linux.dat: i386/32bit-core.xml i386/32bit-linux.xml
$(outdir)/i386/amd64-avx.dat: i386/64bit-core.xml i386/64bit-avx.xml
@@ -285,10 +286,10 @@ $(outdir)/i386/amd64-mpx-linux.dat: i386/64bit-core.xml i386/64bit-avx.xml \
$(outdir)/i386/amd64-mpx.dat: i386/64bit-core.xml i386/64bit-avx.xml \
i386/64bit-mpx.xml
$(outdir)/i386/amd64-avx512.dat: i386/64bit-core.xml i386/64bit-avx.xml \
- i386/64bit-mpx.xml i386/64bit-avx512.xml
+ i386/64bit-mpx.xml i386/64bit-avx512.xml i386/64bit-pkeys.xml
$(outdir)/i386/amd64-avx512-linux.dat: i386/64bit-core.xml i386/64bit-avx.xml \
i386/64bit-mpx.xml i386/64bit-avx512.xml \
- i386/64bit-linux.xml
+ i386/64bit-linux.xml i386/64bit-pkeys.xml
$(outdir)/i386/x32.dat: i386/x32-core.xml i386/64bit-sse.xml
$(outdir)/i386/x32-linux.dat: i386/x32-core.xml i386/64bit-sse.xml \
i386/64bit-linux.xml
diff --git a/gdb/features/i386/32bit-pkeys.xml b/gdb/features/i386/32bit-pkeys.xml
new file mode 100644
index 0000000..d4702e2
--- /dev/null
+++ b/gdb/features/i386/32bit-pkeys.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2015 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. -->
+
+
+<feature name="org.gnu.gdb.i386.pkeys">
+
+ <reg name="pkru" bitsize="32" type="uint32"/>
+
+</feature>
diff --git a/gdb/features/i386/64bit-pkeys.xml b/gdb/features/i386/64bit-pkeys.xml
new file mode 100644
index 0000000..d4702e2
--- /dev/null
+++ b/gdb/features/i386/64bit-pkeys.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2015 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. -->
+
+
+<feature name="org.gnu.gdb.i386.pkeys">
+
+ <reg name="pkru" bitsize="32" type="uint32"/>
+
+</feature>
diff --git a/gdb/features/i386/amd64-avx512-linux.c b/gdb/features/i386/amd64-avx512-linux.c
index 61a547f..021da20 100644
--- a/gdb/features/i386/amd64-avx512-linux.c
+++ b/gdb/features/i386/amd64-avx512-linux.c
@@ -318,5 +318,8 @@ initialize_tdesc_amd64_avx512_linux (void)
tdesc_create_reg (feature, "zmm30h", 150, 1, NULL, 256, "v2ui128");
tdesc_create_reg (feature, "zmm31h", 151, 1, NULL, 256, "v2ui128");

+ feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pkeys");
+ tdesc_create_reg (feature, "pkru", 152, 1, NULL, 32, "uint32");
+
tdesc_amd64_avx512_linux = result;
}
diff --git a/gdb/features/i386/amd64-avx512-linux.xml b/gdb/features/i386/amd64-avx512-linux.xml
index a1878a4..ad2300c 100644
--- a/gdb/features/i386/amd64-avx512-linux.xml
+++ b/gdb/features/i386/amd64-avx512-linux.xml
@@ -17,4 +17,5 @@
<xi:include href="64bit-avx.xml"/>
<xi:include href="64bit-mpx.xml"/>
<xi:include href="64bit-avx512.xml"/>
+ <xi:include href="64bit-pkeys.xml"/>
</target>
diff --git a/gdb/features/i386/amd64-avx512.c b/gdb/features/i386/amd64-avx512.c
index c11bcad..4ffb815 100644
--- a/gdb/features/i386/amd64-avx512.c
+++ b/gdb/features/i386/amd64-avx512.c
@@ -313,5 +313,8 @@ initialize_tdesc_amd64_avx512 (void)
tdesc_create_reg (feature, "zmm30h", 149, 1, NULL, 256, "v2ui128");
tdesc_create_reg (feature, "zmm31h", 150, 1, NULL, 256, "v2ui128");

+ feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pkeys");
+ tdesc_create_reg (feature, "pkru", 151, 1, NULL, 32, "uint32");
+
tdesc_amd64_avx512 = result;
}
diff --git a/gdb/features/i386/amd64-avx512.xml b/gdb/features/i386/amd64-avx512.xml
index 90c0e6d..c6f6ed4 100644
--- a/gdb/features/i386/amd64-avx512.xml
+++ b/gdb/features/i386/amd64-avx512.xml
@@ -15,4 +15,5 @@
<xi:include href="64bit-avx.xml"/>
<xi:include href="64bit-mpx.xml"/>
<xi:include href="64bit-avx512.xml"/>
+ <xi:include href="64bit-pkeys.xml"/>
</target>
diff --git a/gdb/features/i386/i386-avx512-linux.c b/gdb/features/i386/i386-avx512-linux.c
index 53926b2..13c23ff 100644
--- a/gdb/features/i386/i386-avx512-linux.c
+++ b/gdb/features/i386/i386-avx512-linux.c
@@ -204,5 +204,8 @@ initialize_tdesc_i386_avx512_linux (void)
tdesc_create_reg (feature, "zmm6h", 70, 1, NULL, 256, "v2ui128");
tdesc_create_reg (feature, "zmm7h", 71, 1, NULL, 256, "v2ui128");

+ feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pkeys");
+ tdesc_create_reg (feature, "pkru", 72, 1, NULL, 32, "uint32");
+
tdesc_i386_avx512_linux = result;
}
diff --git a/gdb/features/i386/i386-avx512-linux.xml b/gdb/features/i386/i386-avx512-linux.xml
index 72887c8..8661946 100644
--- a/gdb/features/i386/i386-avx512-linux.xml
+++ b/gdb/features/i386/i386-avx512-linux.xml
@@ -17,4 +17,5 @@
<xi:include href="32bit-avx.xml"/>
<xi:include href="32bit-mpx.xml"/>
<xi:include href="32bit-avx512.xml"/>
+ <xi:include href="32bit-pkeys.xml"/>
</target>
diff --git a/gdb/features/i386/i386-avx512.c b/gdb/features/i386/i386-avx512.c
index 2f99499..0f0d22d 100644
--- a/gdb/features/i386/i386-avx512.c
+++ b/gdb/features/i386/i386-avx512.c
@@ -199,5 +199,8 @@ initialize_tdesc_i386_avx512 (void)
tdesc_create_reg (feature, "zmm6h", 69, 1, NULL, 256, "v2ui128");
tdesc_create_reg (feature, "zmm7h", 70, 1, NULL, 256, "v2ui128");

+ feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pkeys");
+ tdesc_create_reg (feature, "pkru", 71, 1, NULL, 32, "uint32");
+
tdesc_i386_avx512 = result;
}
diff --git a/gdb/features/i386/i386-avx512.xml b/gdb/features/i386/i386-avx512.xml
index 525d954..eff3fd7 100644
--- a/gdb/features/i386/i386-avx512.xml
+++ b/gdb/features/i386/i386-avx512.xml
@@ -15,4 +15,5 @@
<xi:include href="32bit-avx.xml"/>
<xi:include href="32bit-mpx.xml"/>
<xi:include href="32bit-avx512.xml"/>
+ <xi:include href="32bit-pkeys.xml"/>
</target>
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 6dfd6e0..ca97f06 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -32,8 +32,8 @@ srv_amd64_linux_regobj="amd64-linux.o amd64-avx-linux.o amd64-avx512-linux.o amd
ipa_i386_linux_regobj=i386-linux-ipa.o
ipa_amd64_linux_regobj=amd64-linux-ipa.o

-srv_i386_32bit_xmlfiles="i386/32bit-core.xml i386/32bit-sse.xml i386/32bit-avx.xml i386/32bit-avx512.xml i386/32bit-mpx.xml"
-srv_i386_64bit_xmlfiles="i386/64bit-core.xml i386/64bit-sse.xml i386/64bit-avx.xml i386/64bit-avx512.xml i386/x32-core.xml i386/64bit-mpx.xml"
+srv_i386_32bit_xmlfiles="i386/32bit-core.xml i386/32bit-sse.xml i386/32bit-avx.xml i386/32bit-avx512.xml i386/32bit-mpx.xml i386/32bit-pkeys.xml"
+srv_i386_64bit_xmlfiles="i386/64bit-core.xml i386/64bit-sse.xml i386/64bit-avx.xml i386/64bit-avx512.xml i386/64bit-pkeys.xml i386/x32-core.xml i386/64bit-mpx.xml"
srv_i386_xmlfiles="i386/i386.xml i386/i386-avx.xml i386/i386-avx512.xml i386/i386-mpx.xml i386/i386-mmx.xml $srv_i386_32bit_xmlfiles"
srv_amd64_xmlfiles="i386/amd64.xml i386/amd64-avx.xml i386/amd64-avx512.xml i386/x32.xml i386/x32-avx.xml i386/x32-avx512.xml i386/amd64-mpx.xml $srv_i386_64bit_xmlfiles"
srv_i386_linux_xmlfiles="i386/i386-linux.xml i386/i386-avx-linux.xml i386/i386-avx512-linux.xml i386/i386-mmx-linux.xml i386/32bit-linux.xml i386/i386-mpx-linux.xml $srv_i386_32bit_xmlfiles"
diff --git a/gdb/gdbserver/i387-fp.c b/gdb/gdbserver/i387-fp.c
index ddfec6e..8885b72 100644
--- a/gdb/gdbserver/i387-fp.c
+++ b/gdb/gdbserver/i387-fp.c
@@ -27,6 +27,7 @@ static const int num_avx512_zmmh_low_registers = 16;
static const int num_avx512_zmmh_high_registers = 16;
static const int num_avx512_ymmh_registers = 16;
static const int num_avx512_xmm_registers = 16;
+static const int num_pkeys_registers = 1;

/* Note: These functions preserve the reserved bits in control registers.
However, gdbserver promptly throws away that information. */
@@ -136,6 +137,10 @@ struct i387_xsave {

/* Space for 16 512-bit zmm16-31 values. */
unsigned char zmmh_high_space[1024];
+
+ /* Space for 1 32-bit PKRU register. The HW XSTATE size for this feature is
+ actually 64 bits, but WRPKRU/RDPKRU instructions ignore upper 32 bits. */
+ unsigned char pkru_space[8];
};

void
@@ -273,7 +278,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
struct i387_xsave *fp = (struct i387_xsave *) buf;
int i;
unsigned long val, val2;
- unsigned int clear_bv;
+ unsigned long clear_bv;
unsigned long long xstate_bv = 0;
char raw[64];
char *p;
@@ -325,6 +330,10 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
for (i = 0; i < num_avx512_ymmh_registers; i++)
memset (((char *) &fp->zmmh_high_space[0]) + 16 + i * 64, 0, 16);
}
+
+ if ((clear_bv & X86_XSTATE_PKRU))
+ for (i = 0; i < num_pkeys_registers; i++)
+ memset (((char *) &fp->pkru_space[0]) + i * 4, 0, 4);
}

/* Check if any x87 registers are changed. */
@@ -497,6 +506,23 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
}
}

+ /* Check if any PKEYS registers are changed. */
+ if ((x86_xcr0 & X86_XSTATE_PKRU))
+ {
+ int pkru_regnum = find_regno (regcache->tdesc, "pkru");
+
+ for (i = 0; i < num_pkeys_registers; i++)
+ {
+ collect_register (regcache, i + pkru_regnum, raw);
+ p = ((char *) &fp->pkru_space[0]) + i * 4;
+ if (memcmp (raw, p, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_PKRU;
+ memcpy (p, raw, 4);
+ }
+ }
+ }
+
/* Update the corresponding bits in xstate_bv if any SSE/AVX
registers are changed. */
fp->xstate_bv |= xstate_bv;
@@ -801,6 +827,23 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
}
}

+ if ((x86_xcr0 & X86_XSTATE_PKRU) != 0)
+ {
+ int pkru_regnum = find_regno (regcache->tdesc, "pkru");
+
+ if ((clear_bv & X86_XSTATE_PKRU) != 0)
+ {
+ for (i = 0; i < num_pkeys_registers; i++)
+ supply_register_zeroed (regcache, i + pkru_regnum);
+ }
+ else
+ {
+ p = (gdb_byte *) &fp->pkru_space[0];
+ for (i = 0; i < num_pkeys_registers; i++)
+ supply_register (regcache, i + pkru_regnum, p + i * 4);
+ }
+ }
+
supply_register_by_name (regcache, "fioff", &fp->fioff);
supply_register_by_name (regcache, "fooff", &fp->fooff);
supply_register_by_name (regcache, "mxcsr", &fp->mxcsr);
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 18adf5e..45ded2e 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -189,7 +189,8 @@ static const int x86_64_regmap[] =
-1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm31 (AVX512) */
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1 /* pkru */
};

#define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0]))
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 63d2648..380db8e 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -59,7 +59,7 @@
(I386_ST0_REGNUM <= (regno) && (regno) < I386_SSE_NUM_REGS)

#define GETXSTATEREGS_SUPPLIES(regno) \
- (I386_ST0_REGNUM <= (regno) && (regno) < I386_AVX512_NUM_REGS)
+ (I386_ST0_REGNUM <= (regno) && (regno) < i386_PKEYS_NUM_REGS)

/* Does the current host support the GETREGS request? */
int have_ptrace_getregs =
diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c
index 86fe09e..1206366 100644
--- a/gdb/i386-linux-tdep.c
+++ b/gdb/i386-linux-tdep.c
@@ -549,6 +549,7 @@ int i386_linux_gregset_reg_offset[] =
-1, -1, /* MPX registers BNDCFGU, BNDSTATUS. */
-1, -1, -1, -1, -1, -1, -1, -1, /* k0 ... k7 (AVX512) */
-1, -1, -1, -1, -1, -1, -1, -1, /* zmm0 ... zmm7 (AVX512) */
+ -1, /* PKRU register */
11 * 4, /* "orig_eax" */
};

diff --git a/gdb/i386-linux-tdep.h b/gdb/i386-linux-tdep.h
index 5ac08d3..b5e17d3 100644
--- a/gdb/i386-linux-tdep.h
+++ b/gdb/i386-linux-tdep.h
@@ -29,7 +29,7 @@
/* Register number for the "orig_eax" pseudo-register. If this
pseudo-register contains a value >= 0 it is interpreted as the
system call number that the kernel is supposed to restart. */
-#define I386_LINUX_ORIG_EAX_REGNUM (I386_ZMM7H_REGNUM + 1)
+#define I386_LINUX_ORIG_EAX_REGNUM (I386_PKRU_REGNUM + 1)

/* Total number of registers for GNU/Linux. */
#define I386_LINUX_NUM_REGS (I386_LINUX_ORIG_EAX_REGNUM + 1)
@@ -56,6 +56,7 @@ extern struct target_desc *tdesc_i386_avx512_linux;
avx512_zmmh_regs0-7[1153..1407]
avx512_zmmh_regs8-15[1408..1663]
avx512_zmm_regs16-31[1664..2687]
+ pkru[2688..2752]
future_state etc
};

diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 6e9bcc3..61395d9 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -118,6 +118,11 @@ static const char *i386_mpx_names[] =
"bnd0raw", "bnd1raw", "bnd2raw", "bnd3raw", "bndcfgu", "bndstatus"
};

+static const char* i386_pkeys_names[] =
+{
+ "pkru"
+};
+
/* Register names for MPX pseudo-registers. */

static const char *i386_bnd_names[] =
@@ -412,6 +417,21 @@ i386_mpx_ctrl_regnum_p (struct gdbarch *gdbarch, int regnum)
return regnum >= 0 && regnum < I387_NUM_MPX_CTRL_REGS;
}

+/* PKRU register? */
+
+int
+i386_pkru_regnum_p (struct gdbarch *gdbarch, int regnum)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int pkru_regnum = tdep->pkru_regnum;
+
+ if (pkru_regnum < 0)
+ return 0;
+
+ regnum -= pkru_regnum;
+ return regnum >= 0 && regnum < I387_NUM_PKEYS_REGS;
+}
+
/* Return the name of register REGNUM, or the empty string if it is
an anonymous register. */

@@ -4454,7 +4474,7 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
ymm_regnum_p, ymmh_regnum_p, ymm_avx512_regnum_p, ymmh_avx512_regnum_p,
bndr_regnum_p, bnd_regnum_p, k_regnum_p, zmm_regnum_p, zmmh_regnum_p,
zmm_avx512_regnum_p, mpx_ctrl_regnum_p, xmm_avx512_regnum_p,
- avx512_p, avx_p, sse_p;
+ avx512_p, avx_p, sse_p, pkru_regnum_p;

/* Don't include pseudo registers, except for MMX, in any register
groups. */
@@ -4471,6 +4491,7 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
if (group == i386_mmx_reggroup)
return mmx_regnum_p;

+ pkru_regnum_p = i386_pkru_regnum_p(gdbarch, regnum);
xmm_regnum_p = i386_xmm_regnum_p (gdbarch, regnum);
xmm_avx512_regnum_p = i386_xmm_avx512_regnum_p (gdbarch, regnum);
mxcsr_regnum_p = i386_mxcsr_regnum_p (gdbarch, regnum);
@@ -4542,7 +4563,8 @@ i386_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
&& !bnd_regnum_p
&& !mpx_ctrl_regnum_p
&& !zmm_regnum_p
- && !zmmh_regnum_p);
+ && !zmmh_regnum_p
+ && !pkru_regnum_p);

return default_register_reggroup_p (gdbarch, regnum, group);
}
@@ -8117,7 +8139,7 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
const struct tdesc_feature *feature_core;

const struct tdesc_feature *feature_sse, *feature_avx, *feature_mpx,
- *feature_avx512;
+ *feature_avx512, *feature_pkeys;
int i, num_regs, valid_p;

if (! tdesc_has_registers (tdesc))
@@ -8140,6 +8162,9 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
/* Try AVX512 registers. */
feature_avx512 = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.avx512");

+ /* Try PKEYS */
+ feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
+
valid_p = 1;

/* The XCR0 bits. */
@@ -8246,6 +8271,22 @@ i386_validate_tdesc_p (struct gdbarch_tdep *tdep,
tdep->mpx_register_names[i]);
}

+ if (feature_pkeys)
+ {
+ tdep->xcr0 |= X86_XSTATE_PKRU;
+ if (tdep->pkru_regnum < 0)
+ {
+ tdep->pkeys_register_names = i386_pkeys_names;
+ tdep->pkru_regnum = I386_PKRU_REGNUM;
+ tdep->num_pkeys_regs = 1;
+ }
+
+ for (i = 0; i < I387_NUM_PKEYS_REGS; i++)
+ valid_p &= tdesc_numbered_register (feature_pkeys, tdesc_data,
+ I387_PKRU_REGNUM (tdep) + i,
+ tdep->pkeys_register_names[i]);
+ }
+
return valid_p;
}

@@ -8434,7 +8475,7 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
/* Even though the default ABI only includes general-purpose registers,
floating-point registers and the SSE registers, we have to leave a
gap for the upper AVX, MPX and AVX512 registers. */
- set_gdbarch_num_regs (gdbarch, I386_AVX512_NUM_REGS);
+ set_gdbarch_num_regs (gdbarch, i386_PKEYS_NUM_REGS);

set_gdbarch_gnu_triplet_regexp (gdbarch, i386_gnu_triplet_regexp);

@@ -8479,6 +8520,10 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->num_ymm_avx512_regs = 0;
tdep->num_xmm_avx512_regs = 0;

+ /* No PKEYS registers */
+ tdep->pkru_regnum = -1;
+ tdep->num_pkeys_regs = 0;
+
tdesc_data = tdesc_data_alloc ();

set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction);
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 95288ba..7ab2f84 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -189,6 +189,15 @@ struct gdbarch_tdep
/* YMM16-31 register names. Only used for tdesc_numbered_register. */
const char **ymm_avx512_register_names;

+ /* Number of PKEYS registers */
+ int num_pkeys_regs;
+
+ /* Register number for PKRU register */
+ int pkru_regnum;
+
+ /* PKEYS register names */
+ const char **pkeys_register_names;
+
/* Target description. */
const struct target_desc *tdesc;

@@ -284,7 +293,8 @@ enum i386_regnum
I386_K0_REGNUM, /* %k0 */
I386_K7_REGNUM = I386_K0_REGNUM + 7,
I386_ZMM0H_REGNUM, /* %zmm0h */
- I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7
+ I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
+ I386_PKRU_REGNUM
};

/* Register numbers of RECORD_REGMAP. */
@@ -324,6 +334,7 @@ enum record_i386_regnum
#define I386_AVX_NUM_REGS (I386_YMM7H_REGNUM + 1)
#define I386_MPX_NUM_REGS (I386_BNDSTATUS_REGNUM + 1)
#define I386_AVX512_NUM_REGS (I386_ZMM7H_REGNUM + 1)
+#define i386_PKEYS_NUM_REGS (I386_PKRU_REGNUM + 1)

/* Size of the largest register. */
#define I386_MAX_REGISTER_SIZE 64
@@ -345,6 +356,7 @@ extern int i386_bnd_regnum_p (struct gdbarch *gdbarch, int regnum);
extern int i386_k_regnum_p (struct gdbarch *gdbarch, int regnum);
extern int i386_zmm_regnum_p (struct gdbarch *gdbarch, int regnum);
extern int i386_zmmh_regnum_p (struct gdbarch *gdbarch, int regnum);
+extern int i386_pkru_regnum_p (struct gdbarch *gdbarch, int regnum);

extern const char *i386_pseudo_register_name (struct gdbarch *gdbarch,
int regnum);
diff --git a/gdb/i387-tdep.c b/gdb/i387-tdep.c
index 9bc45fb..7ecc738 100644
--- a/gdb/i387-tdep.c
+++ b/gdb/i387-tdep.c
@@ -888,6 +888,19 @@ static int xsave_avx512_zmm_h_offset[] =
#define XSAVE_AVX512_ZMM_H_ADDR(tdep, xsave, regnum) \
(xsave + xsave_avx512_zmm_h_offset[regnum - I387_ZMM0H_REGNUM (tdep)])

+/* At xsave_pkeys_offset[REGNUM] you find the offset to the location
+ of the PKRU register data structure used by the "xsave"
+ instruction where GDB register REGNUM is stored. */
+
+static int xsave_pkeys_offset[] =
+{
+2688 + 0 * 8 /* %pkru (64 bits in XSTATE, 32-bit actually used by
+ instructions and applications). */
+};
+
+#define XSAVE_PKEYS_ADDR(tdep, xsave, regnum) \
+(xsave + xsave_pkeys_offset[regnum - I387_PKRU_REGNUM (tdep)])
+
/* Similar to i387_supply_fxsave, but use XSAVE extended state. */

void
@@ -898,7 +911,7 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
const gdb_byte *regs = (const gdb_byte *) xsave;
int i;
- unsigned int clear_bv;
+ unsigned long clear_bv;
static const gdb_byte zero[MAX_REGISTER_SIZE] = { 0 };
enum
{
@@ -911,8 +924,9 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
avx512_zmm_h = 0x20,
avx512_ymmh_avx512 = 0x40,
avx512_xmm_avx512 = 0x80,
+ pkeys = 0x100,
all = x87 | sse | avxh | mpx | avx512_k | avx512_zmm_h
- | avx512_ymmh_avx512 | avx512_xmm_avx512
+ | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys
} regclass;

gdb_assert (regs != NULL);
@@ -921,6 +935,9 @@ i387_supply_xsave (struct regcache *regcache, int regnum,

if (regnum == -1)
regclass = all;
+ else if (regnum >= I387_PKRU_REGNUM (tdep)
+ && regnum < I387_PKEYSEND_REGNUM (tdep))
+ regclass = pkeys;
else if (regnum >= I387_ZMM0H_REGNUM (tdep)
&& regnum < I387_ZMMENDH_REGNUM (tdep))
regclass = avx512_zmm_h;
@@ -951,7 +968,8 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
if (regclass != none)
{
/* Get `xstat_bv'. */
- const gdb_byte *xstate_bv_p = XSAVE_XSTATE_BV_ADDR (regs);
+ const unsigned long *xstate_bv_p =
+ (unsigned long*) XSAVE_XSTATE_BV_ADDR (regs);

/* The supported bits in `xstat_bv' are 1 byte. Clear part in
vector registers if its bit in xstat_bv is zero. */
@@ -974,6 +992,14 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
case none:
break;

+ case pkeys:
+ if ((clear_bv & X86_XSTATE_PKRU))
+ regcache_raw_supply (regcache, regnum, zero);
+ else
+ regcache_raw_supply (regcache, regnum,
+ XSAVE_PKEYS_ADDR (tdep, regs, regnum));
+ return;
+
case avx512_zmm_h:
if ((clear_bv & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
regcache_raw_supply (regcache, regnum, zero);
@@ -1039,6 +1065,26 @@ i387_supply_xsave (struct regcache *regcache, int regnum,
return;

case all:
+ /* Handle PKEYS registers. */
+ if ((tdep->xcr0 & X86_XSTATE_PKRU))
+ {
+ if ((clear_bv & X86_XSTATE_PKRU))
+ {
+ for (i = I387_PKRU_REGNUM (tdep);
+ i < I387_PKEYSEND_REGNUM (tdep);
+ i++)
+ regcache_raw_supply (regcache, i, zero);
+ }
+ else
+ {
+ for (i = I387_PKRU_REGNUM (tdep);
+ i < I387_PKEYSEND_REGNUM (tdep);
+ i++)
+ regcache_raw_supply (regcache, i,
+ XSAVE_PKEYS_ADDR (tdep, regs, i));
+ }
+ }
+
/* Handle the upper ZMM registers. */
if ((tdep->xcr0 & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
{
@@ -1283,8 +1329,9 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
avx512_zmm_h = 0x40 | check,
avx512_ymmh_avx512 = 0x80 | check,
avx512_xmm_avx512 = 0x100 | check,
+ pkeys = 0x200 | check,
all = x87 | sse | avxh | mpx | avx512_k | avx512_zmm_h
- | avx512_ymmh_avx512 | avx512_xmm_avx512
+ | avx512_ymmh_avx512 | avx512_xmm_avx512 | pkeys
} regclass;

gdb_assert (tdep->st0_regnum >= I386_ST0_REGNUM);
@@ -1292,6 +1339,9 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,

if (regnum == -1)
regclass = all;
+ else if (regnum >= I387_PKRU_REGNUM (tdep)
+ && regnum < I387_PKEYSEND_REGNUM (tdep))
+ regclass = pkeys;
else if (regnum >= I387_ZMM0H_REGNUM (tdep)
&& regnum < I387_ZMMENDH_REGNUM (tdep))
regclass = avx512_zmm_h;
@@ -1333,15 +1383,21 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
if ((regclass & check))
{
gdb_byte raw[I386_MAX_REGISTER_SIZE];
- gdb_byte *xstate_bv_p = XSAVE_XSTATE_BV_ADDR (regs);
- unsigned int xstate_bv = 0;
+ unsigned long *xstate_bv_p =
+ (unsigned long*) XSAVE_XSTATE_BV_ADDR (regs);
+ unsigned long xstate_bv = 0;
/* The supported bits in `xstat_bv' are 1 byte. */
- unsigned int clear_bv = (~(*xstate_bv_p)) & tdep->xcr0;
+ unsigned long clear_bv = (~(*xstate_bv_p)) & tdep->xcr0;
gdb_byte *p;

/* Clear register set if its bit in xstat_bv is zero. */
if (clear_bv)
{
+ if ((clear_bv & X86_XSTATE_PKRU))
+ for (i = I387_PKRU_REGNUM (tdep);
+ i < I387_PKEYSEND_REGNUM (tdep); i++)
+ memset (XSAVE_PKEYS_ADDR (tdep, regs, i), 0, 4);
+
if ((clear_bv & X86_XSTATE_BNDREGS))
for (i = I387_BND0R_REGNUM (tdep);
i < I387_BNDCFGU_REGNUM (tdep); i++)
@@ -1390,6 +1446,20 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,

if (regclass == all)
{
+ /* Check if any PKEYS registers are changed. */
+ if ((tdep->xcr0 & X86_XSTATE_PKRU))
+ for (i = I387_PKRU_REGNUM (tdep);
+ i < I387_PKEYSEND_REGNUM (tdep); i++)
+ {
+ regcache_raw_collect (regcache, i, raw);
+ p = XSAVE_PKEYS_ADDR (tdep, regs, i);
+ if (memcmp (raw, p, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_PKRU;
+ memcpy (p, raw, 4);
+ }
+ }
+
/* Check if any ZMMH registers are changed. */
if ((tdep->xcr0 & (X86_XSTATE_ZMM_H | X86_XSTATE_ZMM)))
for (i = I387_ZMM0H_REGNUM (tdep);
@@ -1525,6 +1595,16 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
internal_error (__FILE__, __LINE__,
_("invalid i387 regclass"));

+ case pkeys:
+ /* This is a PKEYS register. */
+ p = XSAVE_PKEYS_ADDR (tdep, regs, regnum);
+ if (memcmp (raw, p, 4) != 0)
+ {
+ xstate_bv |= X86_XSTATE_PKRU;
+ memcpy (p, raw, 4);
+ }
+ break;
+
case avx512_zmm_h:
/* This is a ZMM register. */
p = XSAVE_AVX512_ZMM_H_ADDR (tdep, regs, regnum);
@@ -1620,7 +1700,7 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
if (xstate_bv)
{
/* The supported bits in `xstat_bv' are 1 byte. */
- *xstate_bv_p |= (gdb_byte) xstate_bv;
+ *xstate_bv_p |= xstate_bv;

switch (regclass)
{
@@ -1639,6 +1719,7 @@ i387_collect_xsave (const struct regcache *regcache, int regnum,
case avx512_zmm_h:
case avx512_ymmh_avx512:
case avx512_xmm_avx512:
+ case pkeys:
/* Register REGNUM has been updated. Return. */
return;
}
diff --git a/gdb/i387-tdep.h b/gdb/i387-tdep.h
index 0132c17..069b074 100644
--- a/gdb/i387-tdep.h
+++ b/gdb/i387-tdep.h
@@ -44,7 +44,9 @@ struct ui_file;
#define I387_NUM_BND_REGS 4
#define I387_NUM_MPX_CTRL_REGS 2
#define I387_NUM_K_REGS 8
+#define I387_NUM_PKEYS_REGS 1

+#define I387_PKRU_REGNUM(tdep) ((tdep)->pkru_regnum)
#define I387_K0_REGNUM(tdep) ((tdep)->k0_regnum)
#define I387_NUM_ZMMH_REGS(tdep) ((tdep)->num_zmm_regs)
#define I387_ZMM0H_REGNUM(tdep) ((tdep)->zmm0h_regnum)
@@ -79,6 +81,9 @@ struct ui_file;
#define I387_XMM_AVX512_END_REGNUM(tdep) \
(I387_XMM16_REGNUM (tdep) + I387_NUM_XMM_AVX512_REGS (tdep))

+#define I387_PKEYSEND_REGNUM(tdep) \
+ (I387_PKRU_REGNUM (tdep) + I387_NUM_PKEYS_REGS)
+
/* Print out the i387 floating point state. */

extern void i387_print_float_info (struct gdbarch *gdbarch,
diff --git a/gdb/nat/x86-gcc-cpuid.h b/gdb/nat/x86-gcc-cpuid.h
index 21b7b7a..192187a 100644
--- a/gdb/nat/x86-gcc-cpuid.h
+++ b/gdb/nat/x86-gcc-cpuid.h
@@ -84,6 +84,10 @@
#define bit_AVX512CD (1 << 28)
#define bit_SHA (1 << 29)

+/* %ecx */
+#define bit_PKU (1 << 3)
+#define bit_OSPKE (1 << 4)
+
/* Extended State Enumeration Sub-leaf (%eax == 13, %ecx == 1) */
#define bit_XSAVEOPT (1 << 0)

diff --git a/gdb/regformats/i386/amd64-avx512-linux.dat b/gdb/regformats/i386/amd64-avx512-linux.dat
index dfc41e2..4a801ed 100644
--- a/gdb/regformats/i386/amd64-avx512-linux.dat
+++ b/gdb/regformats/i386/amd64-avx512-linux.dat
@@ -155,3 +155,4 @@ expedite:rbp,rsp,rip
256:zmm29h
256:zmm30h
256:zmm31h
+32:pkru
diff --git a/gdb/regformats/i386/amd64-avx512.dat b/gdb/regformats/i386/amd64-avx512.dat
index 1f6c458..d4fff1d 100644
--- a/gdb/regformats/i386/amd64-avx512.dat
+++ b/gdb/regformats/i386/amd64-avx512.dat
@@ -154,3 +154,4 @@ expedite:rbp,rsp,rip
256:zmm29h
256:zmm30h
256:zmm31h
+32:pkru
diff --git a/gdb/regformats/i386/i386-avx512-linux.dat b/gdb/regformats/i386/i386-avx512-linux.dat
index 8fe70b9..0a05bf5 100644
--- a/gdb/regformats/i386/i386-avx512-linux.dat
+++ b/gdb/regformats/i386/i386-avx512-linux.dat
@@ -75,3 +75,4 @@ expedite:ebp,esp,eip
256:zmm5h
256:zmm6h
256:zmm7h
+32:pkru
diff --git a/gdb/regformats/i386/i386-avx512.dat b/gdb/regformats/i386/i386-avx512.dat
index 7e1fe93..375f79d 100644
--- a/gdb/regformats/i386/i386-avx512.dat
+++ b/gdb/regformats/i386/i386-avx512.dat
@@ -74,3 +74,4 @@ expedite:ebp,esp,eip
256:zmm5h
256:zmm6h
256:zmm7h
+32:pkru
diff --git a/gdb/testsuite/gdb.arch/i386-pkru.c b/gdb/testsuite/gdb.arch/i386-pkru.c
new file mode 100644
index 0000000..6c512b6
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-pkru.c
@@ -0,0 +1,97 @@
+/* Test program for PKEYS registers.
+
+ Copyright 2015 Free Software Foundation, Inc.
+
+ Contributed by Intel Corp. <michael.sturm@intel.com>
+
+ 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 <stdio.h>
+#include "x86-cpuid.h"
+
+#ifndef NOINLINE
+#define NOINLINE __attribute__ ((noinline))
+#endif
+
+unsigned int have_pkru (void) NOINLINE;
+
+static inline unsigned long
+rdpkru(void)
+{
+ unsigned int eax, edx;
+ unsigned int ecx = 0;
+ unsigned int pkru;
+
+ asm volatile(".byte 0x0f,0x01,0xee\n\t"
+ : "=a" (eax), "=d" (edx)
+ : "c" (ecx));
+ pkru = eax;
+ return pkru;
+}
+
+static inline void
+wrpkru(unsigned int pkru)
+{
+ unsigned int eax = pkru;
+ unsigned int ecx = 0;
+ unsigned int edx = 0;
+
+ asm volatile(".byte 0x0f,0x01,0xef\n\t"
+ : : "a" (eax), "c" (ecx), "d" (edx));
+}
+
+
+unsigned int NOINLINE
+have_pkru (void)
+{
+ unsigned int eax, ebx, ecx, edx;
+
+ if (!__get_cpuid (1, &eax, &ebx, &ecx, &edx))
+ return 0;
+
+ if ((ecx & bit_OSXSAVE) == bit_OSXSAVE)
+ {
+ if (__get_cpuid_max (0, NULL) < 7)
+ return 0;
+
+ __cpuid_count (7, 0, eax, ebx, ecx, edx);
+
+ if ((ecx & bit_PKU) == bit_PKU)
+ return 1;
+ else
+ return 0;
+ }
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ unsigned int wr_value = 0x12345678;
+ unsigned int rd_value = 0x0;
+
+ if (have_pkru ())
+ {
+#ifdef __x86_64__
+ wrpkru(wr_value);
+ asm ("nop\n\t"); /* break here 1. */
+
+ rd_value = rdpkru();
+ asm ("nop\n\t"); /* break here 2. */
+#endif
+ }
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/i386-pkru.exp b/gdb/testsuite/gdb.arch/i386-pkru.exp
new file mode 100644
index 0000000..b35d81b
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/i386-pkru.exp
@@ -0,0 +1,80 @@
+# Copyright 2015 Free Software Foundation, Inc.
+#
+# Contributed by Intel Corp. <michael.sturm@intel.com>
+#
+# 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/>.
+
+if { ![istarget i?86-*-*] && ![istarget x86_64-*-* ] } {
+ verbose "Skipping x86 PK tests."
+ return
+}
+
+standard_testfile
+
+if { ![istarget i?86-*-*] && ![istarget x86_64-*-* ] } {
+ verbose "Skipping x86 protection key feature tests."
+ return
+}
+
+set comp_flags "-I${srcdir}/../nat/"
+
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile} \
+ [list debug nowarnings additional_flags=${comp_flags}]] } {
+ return -1
+}
+
+if ![runto_main] {
+ untested "could not run to main"
+ return -1
+}
+
+set supports_pkru 0
+set test "probe PKRU support"
+gdb_test_multiple "print have_pkru()" $test {
+ -re ".. = 1\r\n$gdb_prompt $" {
+ pass $test
+ set supports_pkru 1
+ }
+ -re ".. = 0\r\n$gdb_prompt $" {
+ pass $test
+ }
+}
+
+if { !$supports_pkru } {
+ unsupported "processor does not support protection key feature."
+ return
+}
+
+# Test pkru register at startup
+set test_string "0"
+
+gdb_test "print \$pkru" $test_string "pkru formating"
+
+# Read values from pseudo registers.
+gdb_breakpoint [ gdb_get_line_number "break here 1" ]
+gdb_continue_to_breakpoint "break here 1" ".*break here 1.*"
+
+set test_string ".*0x12345678.*"
+gdb_test "info register pkru" ".*pkru$test_string" "Read pkru register"
+
+set test_string ".*0x44444444.*"
+gdb_test "print /x \$pkru = 0x44444444" "= 0x44444444" "Set pkru value"
+gdb_test "info register pkru" ".*pkru$test_string" "Read value after setting value"
+
+gdb_breakpoint [ gdb_get_line_number "break here 2" ]
+gdb_continue_to_breakpoint "break here 2" ".*break here 2.*"
+
+gdb_test "print /x rd_value" ".*" "program variable after reading pkru"
+
+send_gdb "quit\n"
--
1.8.4.2