diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index b3c1392..44c160c 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -33,22 +33,6 @@
 # define GLIBC_TUNABLES "GLIBC_TUNABLES"
 #endif
 
-/* Compare environment or tunable names, bounded by the name hardcoded in
-   glibc.  */
-static bool
-is_name (const char *orig, const char *envname)
-{
-  for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
-    if (*orig != *envname)
-      break;
-
-  /* The ENVNAME is immediately followed by a value.  */
-  if (*orig == '\0' && *envname == '=')
-    return true;
-  else
-    return false;
-}
-
 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 static char *
 tunables_strdup (const char *in)
@@ -235,7 +219,7 @@ parse_tunables (char *tunestr, char *valstring)
 	{
 	  tunable_t *cur = &tunable_list[i];
 
-	  if (is_name (cur->name, name))
+	  if (tunable_is_name (cur->name, name))
 	    {
 	      /* If we are in a secure context (AT_SECURE) then ignore the tunable
 		 unless it is explicitly marked as secure.  Tunable values take
@@ -318,7 +302,7 @@ __tunables_init (char **envp)
 			       &prev_envp)) != NULL)
     {
 #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
-      if (is_name (GLIBC_TUNABLES, envname))
+      if (tunable_is_name (GLIBC_TUNABLES, envname))
 	{
 	  char *new_env = tunables_strdup (envname);
 	  if (new_env != NULL)
@@ -341,7 +325,7 @@ __tunables_init (char **envp)
 	  const char *name = cur->env_alias;
 
 	  /* We have a match.  Initialize and move on to the next line.  */
-	  if (is_name (name, envname))
+	  if (tunable_is_name (name, envname))
 	    {
 	      /* For AT_SECURE binaries, we need to check the security settings of
 		 the tunable and decide whether we read the value and also whether
@@ -356,7 +340,7 @@ __tunables_init (char **envp)
 
 		      while (*ep != NULL)
 			{
-			  if (is_name (name, *ep))
+			  if (tunable_is_name (name, *ep))
 			    {
 			      char **dp = ep;
 
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 6c49dcb..c92882a 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -111,5 +111,22 @@ rtld_hidden_proto (__tunable_get_val)
 # define TUNABLES_FRONTEND_valstring 1
 /* The default value for TUNABLES_FRONTEND.  */
 # define TUNABLES_FRONTEND_yes TUNABLES_FRONTEND_valstring
+
+/* Compare two name strings, bounded by the name hardcoded in glibc.  */
+static inline bool
+__always_inline
+tunable_is_name (const char *orig, const char *envname)
+{
+  for (;*orig != '\0' && *envname != '\0'; envname++, orig++)
+    if (*orig != *envname)
+      break;
+
+  /* The ENVNAME is immediately followed by a value.  */
+  if (*orig == '\0' && *envname == '=')
+    return true;
+  else
+    return false;
+}
+
 #endif
 #endif
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 689e894..c242194 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -231,6 +231,14 @@ the ones in @code{sysdeps/x86/cpu-features.h}.
 This tunable is specific to i386 and x86-64.
 @end deftp
 
+@deftp Tunable glibc.tune.cpu
+The @code{glibc.tune.cpu=xxx} tunable allows the user to tell @theglibc{} to
+assume that the CPU is @code{xxx} where xxx may have one of these values:
+@code{generic}, @code{thunderx}, @code{falkor}.
+
+This tunable is specific to aarch64.
+@end deftp
+
 @deftp Tunable glibc.tune.x86_data_cache_size
 The @code{glibc.tune.x86_data_cache_size} tunable allows the user to set
 data cache size in bytes for use in memory and string routines.
diff --git a/sysdeps/aarch64/dl-tunables.list b/sysdeps/aarch64/dl-tunables.list
new file mode 100644
index 0000000..b1da2bf
--- /dev/null
+++ b/sysdeps/aarch64/dl-tunables.list
@@ -0,0 +1,25 @@
+# aarch64 specific tunables.
+# Copyright (C) 2017 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/>.
+
+glibc {
+  tune {
+    cpu {
+      type: STRING
+    }
+  }
+}
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index ef6eecd..5be6ff6 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -20,18 +20,54 @@
 #include <sys/auxv.h>
 #include <elf/dl-hwcaps.h>
 
+#if HAVE_TUNABLES
+struct cpu_list
+{
+  const char *name;
+  uint64_t midr;
+};
+
+static struct cpu_list cpu_list[] = {
+      {"falkor",	0x510FC000},
+      {"thunderx",	0x430F0A10},
+      {"generic", 	0x0}
+};
+
+static uint64_t
+get_midr_from_mcpu (const char *mcpu)
+{
+  for (int i = 0; i < sizeof (cpu_list) / sizeof (struct cpu_list); i++)
+    if (tunable_is_name (mcpu, cpu_list[i].name) == 0)
+      return cpu_list[i].midr;
+
+  return UINT64_MAX;
+}
+#endif
+
 static inline void
 init_cpu_features (struct cpu_features *cpu_features)
 {
   uint64_t hwcap_mask = GET_HWCAP_MASK();
   uint64_t hwcap = GLRO (dl_hwcap) & hwcap_mask;
 
-  if (hwcap & HWCAP_CPUID)
+  register uint64_t midr = UINT_MAX;
+
+#if HAVE_TUNABLES
+  /* Get the tunable override.  */
+  const char *mcpu = TUNABLE_GET (glibc, tune, mcpu, const char *, NULL);
+  if (mcpu != NULL)
+    midr = get_midr_from_mcpu (mcpu);
+#endif
+
+  /* If there was no useful tunable override, query the MIDR if the kernel
+     allows it.  */
+  if (midr == UINT_MAX)
     {
-      register uint64_t id = 0;
-      asm volatile ("mrs %0, midr_el1" : "=r"(id));
-      cpu_features->midr_el1 = id;
+      if (hwcap & HWCAP_CPUID)
+	asm volatile ("mrs %0, midr_el1" : "=r"(midr));
+      else
+	midr = 0;
     }
-  else
-    cpu_features->midr_el1 = 0;
+
+  cpu_features->midr_el1 = midr;
 }
