2015-06-08  Carlos Eduardo Seo  <cseo@linux.vnet.ibm.com>

	This patch adds a new feature for powerpc. In order to get faster
	access to the HWCAP/HWCAP2 bits, we now store them in the TCB, so
	we don't have to deal with the overhead of reading them via the
	auxiliary vector. A new API is published in ppc.h for get/set the
	bits.

	* sysdeps/powerpc/nptl/tcb-offsets.sym: Added new offests
	for HWCAP and HWCAP2 in the TCB.
	* sysdeps/powerpc/nptl/tls.h: New functionality - stores
	the HWCAP and HWCAP2 in the TCB.
	(dtv): Added new fields for HWCAP and HWCAP2.
	(TLS_INIT_TP): Included calls to add the hwcap/hwcap2
	values in the TCB in TP initialization.
	(TLS_DEFINE_INIT_TP): Likewise.
	(THREAD_GET_HWCAP): New macro.
	(THREAD_SET_HWCAP): Likewise.
	(THREAD_GET_HWCAP2): Likewise.
	(THREAD_SET_HWCAP2): Likewise.
	* sysdeps/powerpc/sys/platform/ppc.h: Added new functions
	for get/set the HWCAP/HWCAP2 values in the TCB.
	(__ppc_get_hwcap): New function.
	(__ppc_get_hwcap2): Likewise.
	* sysdeps/powerpc/test-get_hwcap.c: Testcase for this
	functionality.
	* sysdeps/powerpc/test-set_hwcap.c: Testcase for this
	functionality.
	* sysdeps/powerpc/Makefile: Added testcases to the Makefile.
	

Index: glibc-working/sysdeps/powerpc/nptl/tcb-offsets.sym
===================================================================
--- glibc-working.orig/sysdeps/powerpc/nptl/tcb-offsets.sym
+++ glibc-working/sysdeps/powerpc/nptl/tcb-offsets.sym
@@ -20,6 +20,8 @@ TAR_SAVE			(offsetof (tcbhead_t, tar_sav
 DSO_SLOT1			(offsetof (tcbhead_t, dso_slot1) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
 DSO_SLOT2			(offsetof (tcbhead_t, dso_slot2) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
 TM_CAPABLE			(offsetof (tcbhead_t, tm_capable) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
+TCB_HWCAP			(offsetof (tcbhead_t, hwcap) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
+TCB_HWCAP2			(offsetof (tcbhead_t, hwcap2) - TLS_TCB_OFFSET - sizeof (tcbhead_t))
 #ifndef __ASSUME_PRIVATE_FUTEX
 PRIVATE_FUTEX_OFFSET		thread_offsetof (header.private_futex)
 #endif
Index: glibc-working/sysdeps/powerpc/nptl/tls.h
===================================================================
--- glibc-working.orig/sysdeps/powerpc/nptl/tls.h
+++ glibc-working/sysdeps/powerpc/nptl/tls.h
@@ -63,6 +63,9 @@ typedef union dtv
    are private.  */
 typedef struct
 {
+  /* Reservation for HWCAP data.  */
+  unsigned int hwcap2;
+  unsigned int hwcap;
   /* Indicate if HTM capable (ISA 2.07).  */
   int tm_capable;
   /* Reservation for Dynamic System Optimizer ABI.  */
@@ -134,7 +137,11 @@ register void *__thread_register __asm__
 # define TLS_INIT_TP(tcbp) \
   ({ 									      \
     __thread_register = (void *) (tcbp) + TLS_TCB_OFFSET;		      \
-    THREAD_SET_TM_CAPABLE (GLRO (dl_hwcap2) & PPC_FEATURE2_HAS_HTM ? 1 : 0);  \
+    unsigned int hwcap = GLRO(dl_hwcap);				      \
+    unsigned int hwcap2 = GLRO(dl_hwcap2);				      \
+    THREAD_SET_TM_CAPABLE (hwcap2 & PPC_FEATURE2_HAS_HTM ? 1 : 0);	      \
+    THREAD_SET_HWCAP (hwcap);						      \
+    THREAD_SET_HWCAP2 (hwcap2);						      \
     NULL;								      \
   })
 
@@ -142,7 +149,11 @@ register void *__thread_register __asm__
 # define TLS_DEFINE_INIT_TP(tp, pd) \
     void *tp = (void *) (pd) + TLS_TCB_OFFSET + TLS_PRE_TCB_SIZE;	      \
     (((tcbhead_t *) ((char *) tp - TLS_TCB_OFFSET))[-1].tm_capable) =	      \
-      THREAD_GET_TM_CAPABLE ();
+      THREAD_GET_TM_CAPABLE ();						      \
+    (((tcbhead_t *) ((char *) tp - TLS_TCB_OFFSET))[-1].hwcap) =	      \
+      THREAD_GET_HWCAP ();						      \
+    (((tcbhead_t *) ((char *) tp - TLS_TCB_OFFSET))[-1].hwcap2) =	      \
+      THREAD_GET_HWCAP2 ();
 
 /* Return the address of the dtv for the current thread.  */
 # define THREAD_DTV() \
@@ -203,6 +214,32 @@ register void *__thread_register __asm__
 # define THREAD_SET_TM_CAPABLE(value) \
     (THREAD_GET_TM_CAPABLE () = (value))
 
+/* hwcap & hwcap2 fields in TCB head.  */
+# define THREAD_GET_HWCAP() \
+    (((tcbhead_t *) ((char *) __thread_register				      \
+		     - TLS_TCB_OFFSET))[-1].hwcap)
+# define THREAD_SET_HWCAP(value) \
+    if (value & PPC_FEATURE_ARCH_2_06)					      \
+      value |= PPC_FEATURE_ARCH_2_05 |					      \
+	       PPC_FEATURE_POWER5_PLUS |				      \
+	       PPC_FEATURE_POWER5 |					      \
+	       PPC_FEATURE_POWER4;					      \
+    else if (value & PPC_FEATURE_ARCH_2_05)				      \
+      value |= PPC_FEATURE_POWER5_PLUS |				      \
+             PPC_FEATURE_POWER5 |					      \
+             PPC_FEATURE_POWER4;					      \
+    else if (value & PPC_FEATURE_POWER5_PLUS)				      \
+      value |= PPC_FEATURE_POWER5 |					      \
+             PPC_FEATURE_POWER4;					      \
+    else if (value & PPC_FEATURE_POWER5)				      \
+      value |= PPC_FEATURE_POWER4;					      \
+    (THREAD_GET_HWCAP () = (value))
+# define THREAD_GET_HWCAP2() \
+    (((tcbhead_t *) ((char *) __thread_register				      \
+                     - TLS_TCB_OFFSET))[-1].hwcap2)
+# define THREAD_SET_HWCAP2(value) \
+    (THREAD_GET_HWCAP2 () = (value))
+
 /* l_tls_offset == 0 is perfectly valid on PPC, so we have to use some
    different value to mean unset l_tls_offset.  */
 # define NO_TLS_OFFSET		-1
Index: glibc-working/sysdeps/powerpc/sys/platform/ppc.h
===================================================================
--- glibc-working.orig/sysdeps/powerpc/sys/platform/ppc.h
+++ glibc-working/sysdeps/powerpc/sys/platform/ppc.h
@@ -23,6 +23,86 @@
 #include <stdint.h>
 #include <bits/ppc.h>
 
+
+/* Get the hwcap/hwcap2 information from the TCB. Offsets taken
+   from tcb-offsets.h.  */
+static inline uint32_t
+__ppc_get_hwcap (void)
+{
+
+  uint32_t __tcb_hwcap;
+
+#ifdef __powerpc64__
+  register unsigned long __tp __asm__ ("r13");
+  __asm__ volatile ("lwz %0,-28772(%1)\n"
+		    : "=r" (__tcb_hwcap)
+		    : "r" (__tp));
+#else
+  register unsigned long __tp __asm__ ("r2");
+  __asm__ volatile ("lwz %0,-28724(%1)\n"
+		    : "=r" (__tcb_hwcap)
+		    : "r" (__tp));
+#endif
+
+  return __tcb_hwcap;
+}
+
+static inline uint32_t
+__ppc_get_hwcap2 (void)
+{
+
+  uint32_t __tcb_hwcap2;
+
+#ifdef __powerpc64__
+  register unsigned long __tp __asm__ ("r13");
+  __asm__ volatile ("lwz %0,-28776(%1)\n"
+		    : "=r" (__tcb_hwcap2)
+		    : "r" (__tp));
+#else
+  register unsigned long __tp __asm__ ("r2");
+  __asm__ volatile ("lwz %0,-28728(%1)\n"
+		    : "=r" (__tcb_hwcap2)
+		    : "r" (__tp));
+#endif
+
+  return __tcb_hwcap2;
+}
+
+/* Set the hwcap/hwcap2 bits into the designated area in the TCB. Offsets
+   taken from tcb-offsets.h.  */
+
+static inline void
+__ppc_set_hwcap (uint32_t __hwcap_mask)
+{
+#ifdef __powerpc64__
+  register unsigned long __tp __asm__ ("r13");
+  __asm__ volatile ("stw %1,-28772(%0)\n"
+		    :
+		    : "r" (__tp), "r" (__hwcap_mask));
+#else
+  register unsigned long __tp __asm__ ("r2");
+  __asm__ volatile ("stw %1,-28724(%0)\n"
+		    :
+		    : "r" (__tp), "r" (__hwcap_mask));
+#endif
+}
+
+static inline void
+__ppc_set_hwcap2 (uint32_t __hwcap2_mask)
+{
+#ifdef __powerpc64__
+  register unsigned long __tp __asm__ ("r13");
+  __asm__ volatile ("stw %1,-28776(%0)\n"
+		    :
+		    : "r" (__tp), "r" (__hwcap2_mask));
+#else
+  register unsigned long __tp __asm__ ("r2");
+  __asm__ volatile ("stw %1,-28728(%0)\n"
+		    :
+		    : "r" (__tp), "r" (__hwcap2_mask));
+#endif
+}
+
 /* Read the Time Base Register.   */
 static inline uint64_t
 __ppc_get_timebase (void)
Index: glibc-working/sysdeps/powerpc/Makefile
===================================================================
--- glibc-working.orig/sysdeps/powerpc/Makefile
+++ glibc-working/sysdeps/powerpc/Makefile
@@ -28,7 +28,7 @@ endif
 
 ifeq ($(subdir),misc)
 sysdep_headers += sys/platform/ppc.h
-tests += test-gettimebase
+tests += test-gettimebase test-get_hwcap test-set_hwcap
 endif
 
 ifneq (,$(filter %le,$(config-machine)))
Index: glibc-working/sysdeps/powerpc/test-get_hwcap.c
===================================================================
--- /dev/null
+++ glibc-working/sysdeps/powerpc/test-get_hwcap.c
@@ -0,0 +1,73 @@
+/* Check __ppc_get_hwcap() functionality
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Tests if the hwcap and hwcap2 data is stored in the TCB.  */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <sys/auxv.h>
+#include <sys/platform/ppc.h>
+
+static int
+do_test (void)
+{
+  uint32_t h1, h2, hwcap, hwcap2;
+
+  h1 = __ppc_get_hwcap ();
+  h2 = __ppc_get_hwcap2 ();
+  hwcap = getauxval(AT_HWCAP);
+  hwcap2 = getauxval(AT_HWCAP2);
+
+  /* hwcap contains only the latest supported ISA, the code checks which is
+     and fills the previous supported ones. This is necessary because the
+     same is done in tls.h when setting the values to the TCB.   */
+
+  if (hwcap & PPC_FEATURE_ARCH_2_06)
+    hwcap |= PPC_FEATURE_ARCH_2_05 | PPC_FEATURE_POWER5_PLUS |
+	     PPC_FEATURE_POWER5 | PPC_FEATURE_POWER4;
+  else if (hwcap & PPC_FEATURE_ARCH_2_05)
+    hwcap |= PPC_FEATURE_POWER5_PLUS | PPC_FEATURE_POWER5 | PPC_FEATURE_POWER4;
+  else if (hwcap & PPC_FEATURE_POWER5_PLUS)
+    hwcap |= PPC_FEATURE_POWER5 | PPC_FEATURE_POWER4;
+  else if (hwcap & PPC_FEATURE_POWER5)
+    hwcap |= PPC_FEATURE_POWER4;
+
+  if ( h1 != hwcap )
+    {
+      printf("Fail: HWCAP is %x. Should be %x\n", h1, hwcap);
+      return 1;
+    }
+
+  if ( h2 != hwcap2 )
+    {
+      printf("Fail: HWCAP2 is %x. Should be %x\n", h2, hwcap2);
+      return 1;
+    }
+
+    printf("Pass: HWCAP and HWCAP2 are correctly set in the TCB.\n");
+
+    return 0;
+
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+
Index: glibc-working/sysdeps/powerpc/test-set_hwcap.c
===================================================================
--- /dev/null
+++ glibc-working/sysdeps/powerpc/test-set_hwcap.c
@@ -0,0 +1,63 @@
+/* Check __ppc_get_hwcap() functionality
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+/* Tests if the hwcap and hwcap2 data can be stored in the TCB
+   via the ppc.h API.  */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <sys/auxv.h>
+#include <sys/platform/ppc.h>
+
+static int
+do_test (void)
+{
+  uint32_t h1, hwcap, hwcap2;
+
+  h1 = 0xDEADBEEF;
+
+  __ppc_set_hwcap(h1);
+  hwcap = __ppc_get_hwcap();
+
+  if ( h1 != hwcap )
+    {
+      printf("Fail: HWCAP is %x. Should be %x\n", h1, hwcap);
+      return 1;
+    }
+
+  __ppc_set_hwcap2(h1);
+  hwcap2 = __ppc_get_hwcap2();
+
+  if ( h1 != hwcap2 )
+    {
+      printf("Fail: HWCAP2 is %x. Should be %x\n", h1, hwcap2);
+      return 1;
+    }
+
+    printf("Pass: HWCAP and HWCAP2 are correctly set in the TCB.\n");
+
+    return 0;
+
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+
