]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
WIP CPUID consistency checks
authorFlorian Weimer <fweimer@redhat.com>
Tue, 28 May 2024 11:12:16 +0000 (13:12 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Thu, 30 May 2024 12:33:36 +0000 (14:33 +0200)
sysdeps/unix/sysv/linux/x86_64/Makefile
sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S [new file with mode: 0644]
sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py [new file with mode: 0644]
sysdeps/x86/Makefile
sysdeps/x86/dl-diagnostics-cpu.c
sysdeps/x86/dl-get-cpu-features.c
sysdeps/x86/dl-x86_cpu_feature_diagnostics.c [new file with mode: 0644]
sysdeps/x86/include/cpu-features.h

index fcbffd81cbaa031d7b75ed29e9b374d342ce9bb2..4fb0dd4d1d84bf6032f1709a4e65b9ff496e6cac 100644 (file)
@@ -15,6 +15,12 @@ gen-as-const-headers += sigaltstack-offsets.sym
 endif
 
 ifeq ($(subdir),elf)
+sysdep-dl-routines += dl-x86_probes
+shared-only-routines += dl-x86_probes
+# Used internally by dl-x86_cpu_feature_diagnostics.c.
+CFLAGS-dl-catch.os += $(rtld-early-cflags)
+CFLAGS-rtld-__longjmp.os += $(rtld-early-cflags)
+
 ifeq (yes,$(enable-x86-isa-level))
 tests += \
   tst-glibc-hwcaps-2 \
diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c b/sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c
new file mode 100644 (file)
index 0000000..6486ebb
--- /dev/null
@@ -0,0 +1,227 @@
+/* CPU diagnostics probing.  Linux/x86-64 version.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <cpu-features.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/wait.h>
+#include <sysdep.h>
+
+static void
+_dl_x86_probe (struct x86_cpu_feature_diagnostics *diag, bool reported,
+               void (*probe) (void))
+{
+  if (reported)
+    diag->reported |= 1ULL << diag->count;
+
+  /* Use fork/waitid for crash handling.  This is simpler than using
+     signal handling: it does not need global data to communicate with
+     the handler, nor building out-of-line helper functions to the
+     baseline ISA, and it avoids dealing differences in sigset_t size.  */
+  long int ret = INTERNAL_SYSCALL_CALL (fork);
+  if (ret == 0)
+    {
+      /* New process that runs the probe.  This may trigger a crash.  */
+      probe ();
+
+      INTERNAL_SYSCALL_CALL (exit_group, 0);
+    }
+  else if (ret > 0)
+    {
+      siginfo_t si;
+      ret = INTERNAL_SYSCALL_CALL (waitid, P_PID, ret, &si, WEXITED, NULL);
+      if (ret >=0 && si.si_status == 0)
+        /* Probe was successful.   */
+        diag->probed |= 1ULL << diag->count;
+    }
+
+  ++diag->count;
+}
+
+void
+_dl_x86_cpu_feature_diagnostics_run (const struct cpu_features *cpu_features,
+                                     struct x86_cpu_feature_diagnostics *diag)
+{
+  /* x86-64-v2 features.  */
+  extern void _dl_x86_probe_cmpxchg16b (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, CMPXCHG16B),
+                 _dl_x86_probe_cmpxchg16b);
+
+  extern void _dl_x86_probe_sahf (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, LAHF64_SAHF64),
+                 _dl_x86_probe_sahf);
+
+  extern void _dl_x86_probe_popcnt (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, POPCNT),
+                 _dl_x86_probe_popcnt);
+
+  extern void _dl_x86_probe_sse3 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSE3),
+                 _dl_x86_probe_sse3);
+
+  extern void _dl_x86_probe_sse4_1 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSE4_1),
+                 _dl_x86_probe_sse4_1);
+
+  extern void _dl_x86_probe_sse4_2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSE4_2),
+                 _dl_x86_probe_sse4_2);
+
+  extern void _dl_x86_probe_ssse3 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SSSE3),
+                 _dl_x86_probe_ssse3);
+
+  /* x86-64-v3 features.  */
+  extern void _dl_x86_probe_avx (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX),
+                 _dl_x86_probe_avx);
+
+  /* AVX probe using xmm registers.  */
+  extern void _dl_x86_probe_avx_xmm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX),
+                 _dl_x86_probe_avx_xmm);
+
+  extern void _dl_x86_probe_avx2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX2),
+                 _dl_x86_probe_avx2);
+
+  extern void _dl_x86_probe_bmi1 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, BMI1),
+                 _dl_x86_probe_bmi1);
+
+  /* Alternative BMI1 probe.  Perhaps harder to mask.  */
+  extern void _dl_x86_probe_bmi1_tzcnt (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, BMI1),
+                 _dl_x86_probe_bmi1_tzcnt);
+
+  extern void _dl_x86_probe_bmi2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, BMI2),
+                 _dl_x86_probe_bmi2);
+
+  extern void _dl_x86_probe_f16c (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, F16C),
+                 _dl_x86_probe_f16c);
+
+  extern void _dl_x86_probe_fma (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, FMA),
+                 _dl_x86_probe_fma);
+
+  /* FMA4 is not part of x86-64-v3, but may produce a useful hint.  */
+  extern void _dl_x86_probe_fma4 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, FMA4),
+                 _dl_x86_probe_fma4);
+
+  extern void _dl_x86_probe_lzcnt (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, LZCNT),
+                 _dl_x86_probe_lzcnt);
+
+  extern void _dl_x86_probe_movbe (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, MOVBE),
+                 _dl_x86_probe_movbe);
+
+  extern void _dl_x86_probe_osxsave (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, OSXSAVE),
+                 _dl_x86_probe_osxsave);
+
+  /* x86-64-v4 features.  */
+  extern void _dl_x86_probe_avx512f (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512F),
+                 _dl_x86_probe_avx512f);
+
+  extern void _dl_x86_probe_avx512bw (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512BW),
+                 _dl_x86_probe_avx512bw);
+
+  extern void _dl_x86_probe_avx512bw_ymm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512BW),
+                 _dl_x86_probe_avx512bw_ymm);
+
+  extern void _dl_x86_probe_avx512cd (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512CD),
+                 _dl_x86_probe_avx512cd);
+
+  extern void _dl_x86_probe_avx512cd_ymm0 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512CD),
+                 _dl_x86_probe_avx512cd_ymm0);
+
+  extern void _dl_x86_probe_avx512vl (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512VL),
+                 _dl_x86_probe_avx512vl);
+
+  /* Other CPU features, not part of microarchitecture levels.  */
+  extern void _dl_x86_probe_adx (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, ADX),
+                 _dl_x86_probe_adx);
+
+  extern void _dl_x86_probe_aes (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AES),
+                 _dl_x86_probe_aes);
+
+  extern void _dl_x86_probe_aes_avx (void) attribute_hidden;
+  _dl_x86_probe (diag,
+                 CPU_FEATURE_USABLE_P (cpu_features, AES)
+                 && CPU_FEATURE_USABLE_P (cpu_features, AVX),
+                 _dl_x86_probe_aes_avx);
+
+  extern void _dl_x86_probe_vaes (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, VAES),
+                 _dl_x86_probe_vaes);
+
+  extern void _dl_x86_probe_sha (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, SHA),
+                 _dl_x86_probe_sha);
+
+  extern void _dl_x86_probe_avx512_vbmi (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI),
+                 _dl_x86_probe_avx512_vbmi);
+
+  extern void _dl_x86_probe_avx512_vbmi_xmm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI),
+                 _dl_x86_probe_avx512_vbmi_xmm);
+
+  extern void _dl_x86_probe_avx512_vbmi2 (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI2),
+                 _dl_x86_probe_avx512_vbmi2);
+
+  extern void _dl_x86_probe_avx512_vbmi2_xmm (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VBMI2),
+                 _dl_x86_probe_avx512_vbmi2_xmm);
+
+  extern void _dl_x86_probe_avx_vnni (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX_VNNI),
+                 _dl_x86_probe_avx_vnni);
+
+  extern void _dl_x86_probe_avx512_vnni (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_VNNI),
+                 _dl_x86_probe_avx512_vnni);
+
+  extern void _dl_x86_probe_avx512_ifma (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, AVX512_IFMA),
+                 _dl_x86_probe_avx512_ifma);
+
+  serialize
+  tpause
+  ptwrite
+  xsusldtrk
+  clmul
+  crc32
+
+  extern void _dl_x86_probe_apx_f (void) attribute_hidden;
+  _dl_x86_probe (diag, CPU_FEATURE_USABLE_P (cpu_features, APX_F),
+                 _dl_x86_probe_apx_f);
+}
diff --git a/sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S b/sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S
new file mode 100644 (file)
index 0000000..e122676
--- /dev/null
@@ -0,0 +1,274 @@
+/* Diagnostics probes for the x86 CPU family.  Generic version.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+ENTRY (_dl_x86_probe_cmpxchg16b)
+       xorl %eax, %eax
+       movq %rax, -8(%rsp)
+       movq %rax, -16(%rsp)
+       xorl %edx, %edx
+       cmpxchg16b -8(%rsp)
+       ret
+END (_dl_x86_probe_cmpxchg16b)
+
+ENTRY (_dl_x86_probe_sahf)
+       xorl %eax, %eax
+       sahf
+       ret
+END (_dl_x86_probe_sahf)
+
+ENTRY (_dl_x86_probe_popcnt)
+       xorl %eax, %eax
+       popcnt %eax, %eax
+       ret
+END (_dl_x86_probe_popcnt)
+
+ENTRY (_dl_x86_probe_sse3)
+       pxor %xmm0, %xmm0
+       addsubpd %xmm0,  %xmm0
+       ret
+END (_dl_x86_probe_sse3)
+
+ENTRY (_dl_x86_probe_sse4_1)
+       pxor %xmm0, %xmm0
+       blendpd $1, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_sse4_1)
+
+ENTRY (_dl_x86_probe_sse4_2)
+       pxor %xmm0, %xmm0
+       pcmpestri $0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_sse4_2)
+
+ENTRY (_dl_x86_probe_ssse3)
+       pxor %xmm0, %xmm0
+       phaddd %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_ssse3)
+
+ENTRY (_dl_x86_probe_avx)
+       vzeroall
+       ret
+END (_dl_x86_probe_avx)
+
+ENTRY (_dl_x86_probe_avx_xmm)
+       pxor %xmm0, %xmm0
+       pxor %xmm1, %xmm1
+       vpxor %xmm0, %xmm1, %xmm2
+       ret
+END (_dl_x86_probe_avx_xmm)
+
+ENTRY (_dl_x86_probe_avx2)
+       vpxor %ymm0, %ymm0, %ymm0
+       vpermd %ymm0, %ymm0, %ymm0
+       ret
+END (_dl_x86_probe_avx2)
+
+ENTRY (_dl_x86_probe_bmi1)
+       xorl  %eax,  %eax
+       andnl %eax, %eax, %eax
+       ret
+END (_dl_x86_probe_bmi1)
+
+ENTRY (_dl_x86_probe_bmi1_tzcnt)
+       xorl %eax, %eax
+       /* Executes as bsfl if unsupported.  */
+       tzcntl %eax, %eax
+       cmp $32, %eax
+       jne 1f
+       ret
+1:
+       ud2
+END (_dl_x86_probe_bmi1_tzcnt)
+
+ENTRY (_dl_x86_probe_bmi2)
+       xorl  %eax,  %eax
+       bzhil %eax, %eax, %eax
+       ret
+END (_dl_x86_probe_bmi2)
+
+ENTRY (_dl_x86_probe_f16c)
+       pxor %xmm0, %xmm0
+       vcvtph2ps %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_f16c)
+
+ENTRY (_dl_x86_probe_fma)
+       pxor %xmm0, %xmm0
+       vfmadd132pd %xmm0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_fma)
+
+ENTRY (_dl_x86_probe_fma4)
+       pxor %xmm0, %xmm0
+       vfmaddpd %xmm0, %xmm0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_fma4)
+
+ENTRY (_dl_x86_probe_lzcnt)
+       xorl %eax, %eax
+       /* Executes as bsrl if unsupported.  */
+       lzcntl %eax, %eax
+       cmp $32, %eax
+       jne 1f
+       ret
+1:
+       ud2
+END (_dl_x86_probe_lzcnt)
+
+ENTRY (_dl_x86_probe_movbe)
+       movbeq (%rsp), %rax
+       ret
+1:
+       ud2
+END (_dl_x86_probe_movbe)
+
+ENTRY (_dl_x86_probe_osxsave)
+       xorl %ecx, %ecx
+       xgetbv
+       ret
+END (_dl_x86_probe_osxsave)
+
+ENTRY (_dl_x86_probe_avx512f)
+       xorl %eax, %eax
+       kmovw %eax, %k0
+       ret
+END (_dl_x86_probe_avx512f)
+
+ENTRY (_dl_x86_probe_avx512bw)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vdbpsadbw $0, %zmm0, %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512bw)
+
+ENTRY (_dl_x86_probe_avx512bw_ymm)
+       vpxorq %ymm0, %ymm0, %ymm0
+       vdbpsadbw $0, %ymm0, %ymm0, %ymm0
+       ret
+END (_dl_x86_probe_avx512bw)
+
+ENTRY (_dl_x86_probe_avx512cd)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vplzcntd %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512cd)
+
+ENTRY (_dl_x86_probe_avx512cd_ymm0)
+       vpxorq %ymm0, %ymm0, %ymm0
+       vplzcntd %ymm0, %ymm0
+       ret
+END (_dl_x86_probe_avx512cd_ymm0)
+
+ENTRY (_dl_x86_probe_avx512dq)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vpmullq %zmm0, %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512dq)
+
+ENTRY (_dl_x86_probe_avx512dq_ymm0)
+       vpxorq %ymm0, %ymm0, %ymm0
+       vpmulld %ymm0, %ymm0, %ymm0
+       ret
+END (_dl_x86_probe_avx512dq_ymm0)
+
+ENTRY (_dl_x86_probe_avx512vl)
+       xorl %eax, %eax
+       vpbroadcastq %rax, %xmm1
+       ret
+END (_dl_x86_probe_avx512vl)
+
+ENTRY (_dl_x86_probe_adx)
+       xorl %eax, %eax
+       adcxl %eax, %eax
+       ret
+END (_dl_x86_probe_adx)
+
+ENTRY (_dl_x86_probe_aes)
+       pxor %xmm0, %xmm0
+       aesenc %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_aes)
+
+ENTRY (_dl_x86_probe_aes_avx)
+       pxor %xmm0, %xmm0
+       vaesenc %xmm0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_aes_avx)
+
+ENTRY (_dl_x86_probe_vaes)
+       vpxor %ymm0, %ymm0, %ymm0
+       vaesenc %ymm0, %ymm0, %ymm0
+       ret
+END (_dl_x86_probe_vaes)
+
+ENTRY (_dl_x86_probe_sha)
+       pxor %xmm0, %xmm0
+       sha1rnds4 $0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_sha)
+
+ENTRY (_dl_x86_probe_avx512_vbmi)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vpermb %zmm0, %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512_vbmi)
+
+ENTRY (_dl_x86_probe_avx512_vbmi_xmm)
+       pxor %xmm0, %xmm0
+       vpermb %xmm0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_avx512_vbmi_xmm)
+
+ENTRY (_dl_x86_probe_avx512_vbmi2)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vpshrdd $1, %zmm0, %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512_vbmi2)
+
+ENTRY (_dl_x86_probe_avx512_vbmi2_xmm)
+       pxor %xmm0, %xmm0
+       vpshrdd $1, %xmm0, %xmm0, %xmm0
+       ret
+END (_dl_x86_probe_avx512_vbmi2_xmm)
+
+ENTRY (_dl_x86_probe_avx_vnni)
+       pxor %xmm0, %xmm0
+       /* Default is to use EVEX encoding.  */
+       /* {vex} vpdpbusd %xmm0, %xmm0, %xmm0 */
+       .byte 0xc4, 0xe2, 0x79, 0x50, 0xc0
+       ret
+END (_dl_x86_probe_avx_vnni)
+
+ENTRY (_dl_x86_probe_avx512_vnni)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vpdpbusd %zmm0, %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512_vnni)
+
+ENTRY (_dl_x86_probe_avx512_ifma)
+       vpxorq %zmm0, %zmm0, %zmm0
+       vpmadd52luq %zmm0, %zmm0, %zmm0
+       ret
+END (_dl_x86_probe_avx512_ifma)
+
+ENTRY (_dl_x86_probe_apx_f)
+       .byte 0x62, 0xf4, 0x7c, 0x18, 0x01, 0xff /* add %edi, %edi, %eax */
+       ret
+END (_dl_x86_probe_apx_f)
diff --git a/sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py b/sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py
new file mode 100644 (file)
index 0000000..a8730d3
--- /dev/null
@@ -0,0 +1,34 @@
+# CPU diagnostics probing.  Generating documentatable for t he manual
+# Copyright (C) 2024 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
+# <https://www.gnu.org/licenses/>.
+
+import re
+import sys
+
+path, = sys.argv[1:]
+
+RE_PROBE = re.compile('^\s+_dl_x86_probe_([a-z0-9_]+)\);$')
+
+bit = 1
+with open(path) as inp:
+    for line in inp:
+        m = RE_PROBE.match(line)
+        if m:
+            name = m.group(1)
+            print('@item 0x{:08x}:{:08x}'.format(bit >> 32, bit & 0xffffffff))
+            print('@code{' + name + '}')
+            bit *= 2
index 5311b594aff62f7cb69e88657383f5ddb3790c06..010f15b1223c2b252f9edc114b888f929a44df47 100644 (file)
@@ -4,7 +4,11 @@ endif
 
 ifeq ($(subdir),elf)
 sysdep_routines += get-cpuid-feature-leaf
-sysdep-dl-routines += dl-get-cpu-features
+sysdep-dl-routines += \
+  dl-get-cpu-features \
+  dl-x86_cpu_feature_diagnostics \
+  # sysdep-dl-routines
+shared-only-routines += dl-x86_cpu_feature_diagnostics
 sysdep_headers += \
   bits/platform/features.h \
   bits/platform/x86.h \
@@ -12,6 +16,7 @@ sysdep_headers += \
 # sysdep_headers
 
 CFLAGS-dl-get-cpu-features.os += $(rtld-early-cflags)
+CFLAGS-dl-x86_cpu_feature_diagnostics.os += $(rtld-early-cflags)
 CFLAGS-get-cpuid-feature-leaf.o += $(no-stack-protector)
 
 tests += \
index ceafde94810b1f1fe8ee39bc269c70df3e66b8d7..5555c473c159a7545d55e033571f33d656e1ec56 100644 (file)
@@ -51,6 +51,14 @@ print_cpu_feature_preferred (const char *label, unsigned int flag)
   _dl_printf("x86.cpu_features.preferred.%s=0x%x\n", label, flag);
 }
 
+static void
+_dl_x86_cpu_feature_diagnostics_print (const char *label,
+                                      unsigned long long int value)
+{
+  _dl_printf("x86.diagnostics.");
+  _dl_diagnostics_print_labeled_value (label, value);
+}
+
 void
 _dl_diagnostics_cpu (void)
 {
@@ -132,6 +140,17 @@ _dl_diagnostics_cpu (void)
          == sizeof (*cpu_features),
       "last cpu_features field has been printed");
 
+  {
+    struct x86_cpu_feature_diagnostics diag;
+    _dl_x86_cpu_feature_diagnostics_init (&diag);
+    _dl_x86_cpu_feature_diagnostics_run (cpu_features, &diag);
+    _dl_x86_cpu_feature_diagnostics_print ("count", diag.count);
+    _dl_x86_cpu_feature_diagnostics_print ("reported", diag.reported);
+    _dl_x86_cpu_feature_diagnostics_print ("probed", diag.probed);
+    _dl_x86_cpu_feature_diagnostics_print ("filtered",
+                                          diag.probed & ~diag.reported);
+  }
+
   _dl_diagnostics_cpuid ();
 }
 
index f36d42d6afc3e45ab9a334865be154e10624ad4f..7961aa3b9d9f4deae56e8d77c1f5875ccf6d2932 100644 (file)
@@ -33,14 +33,20 @@ void (*const __x86_cpu_features_p) (void) attribute_hidden
   = __x86_cpu_features;
 
 _Noreturn static void __attribute__ ((unused))
-_dl_x86_init_cpu_failure (const struct cpu_features *cpu_features, int level)
+_dl_x86_init_cpu_failure (const struct cpu_features *cpu_features,
+                         const char *label)
 {
-  if (level == 5)
-    _dl_fatal_printf ("\
-Fatal glibc error: CPU does not support APX\n");
-  else
-    _dl_fatal_printf ("\
-Fatal glibc error: CPU does not support x86-64-v%d\n", level);
+  struct x86_cpu_feature_diagnostics diag;
+  _dl_x86_cpu_feature_diagnostics_init (&diag);
+  _dl_x86_cpu_feature_diagnostics_run (cpu_features, &diag);
+
+  _dl_fatal_printf ("\
+Fatal glibc error: CPU does not support %s [%u 0x%x:%x 0x%x:%x]\n",
+                   label, diag.count,
+                   (unsigned int) (diag.reported >> 32),
+                   (unsigned int) diag.reported,
+                   (unsigned int) (diag.probed >> 32),
+                   (unsigned int) diag.probed);
 }
 
 void
@@ -58,23 +64,23 @@ _dl_x86_init_cpu_features (void)
   && defined GCCMACRO__SSE3__ && defined GCCMACRO__SSSE3__             \
   && defined GCCMACRO__SSE4_1__ && defined GCCMACRO__SSE4_2__
       if (!(cpu_features->isa_1 & GNU_PROPERTY_X86_ISA_1_V2))
-       _dl_x86_init_cpu_failure (cpu_features, 2);
+       _dl_x86_init_cpu_failure (cpu_features, "x86-64-v2");
 #   if defined GCCMACRO__AVX__ && defined GCCMACRO__AVX2__ \
   && defined GCCMACRO__F16C__ && defined GCCMACRO__FMA__   \
   && defined GCCMACRO__LZCNT__ && defined HAVE_X86_MOVBE
       if (!(cpu_features->isa_1 & GNU_PROPERTY_X86_ISA_1_V3))
-       _dl_x86_init_cpu_failure (cpu_features, 3);
+       _dl_x86_init_cpu_failure (cpu_features, "x86-64-v3");
 #    if defined GCCMACRO__AVX512F__ && defined GCCMACRO__AVX512BW__ \
      && defined GCCMACRO__AVX512CD__ && defined GCCMACRO__AVX512DQ__ \
      && defined GCCMACRO__AVX512VL__
       if (!(cpu_features->isa_1 & GNU_PROPERTY_X86_ISA_1_V4))
-       _dl_x86_init_cpu_failure (cpu_features, 4);
+       _dl_x86_init_cpu_failure (cpu_features, "x86-64-v4");
 #    endif /* ISA level 4 */
 #   endif /* ISA level 3 */
 #  endif /* ISA level 2 */
 # ifdef GCCMACRO__APX_F__
       if (!CPU_FEATURE_USABLE_P (cpu_features, APX_F))
-       _dl_x86_init_cpu_failure (cpu_features, 5);
+       _dl_x86_init_cpu_failure (cpu_features, "APX");
 # endif
 # endif /* IS_IN (rtld) */
     }
diff --git a/sysdeps/x86/dl-x86_cpu_feature_diagnostics.c b/sysdeps/x86/dl-x86_cpu_feature_diagnostics.c
new file mode 100644 (file)
index 0000000..bb7ff9c
--- /dev/null
@@ -0,0 +1,26 @@
+/* CPU diagnostics probing.  Generic x86 version.
+   Copyright (C) 2024 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
+   <https://www.gnu.org/licenses/>.  */
+
+#include <cpu-features
+
+/* The generic version does not have any probes.  */
+void
+_dl_x86_cpu_feature_diagnostics_run (const struct cpu_features *cpu_features,
+                                     struct x86_cpu_feature_diagnostics *diag)
+{
+}
index cd7bd27cf35959fddb5a4388ba0bb3cf6259a62c..2eea95d56b6affdc0cfbf882ac522b3ddb4640ff 100644 (file)
@@ -987,6 +987,29 @@ extern const struct cpu_features *_dl_x86_get_cpu_features (void)
 
 #define __get_cpu_features() _dl_x86_get_cpu_features()
 
+/* Used to store diagnostic information for startup failure reporting.
+   See _dl_x86_init_cpu_failure in sysdeps/x86/dl-diagnostics-cpu.c.  */
+struct x86_cpu_feature_diagnostics
+{
+  unsigned int count;          /* Bits recorded.  */
+  unsigned long long int reported; /* From CPUID.  */
+  unsigned long long int probed;  /* From execution probing.  */
+};
+
+/* Initialize *DIAG prior to the diagnostics run below.   */
+static inline void
+_dl_x86_cpu_feature_diagnostics_init (struct x86_cpu_feature_diagnostics *diag)
+{
+  diag->count = 0;
+  diag->reported = 0;
+  diag->probed = 0;
+}
+
+/* Updated the diagnostics with CPUID and execution probing information.  */
+void _dl_x86_cpu_feature_diagnostics_run (const struct cpu_features *,
+                                         struct x86_cpu_feature_diagnostics *)
+  attribute_hidden;
+
 #if defined (_LIBC) && !IS_IN (nonlib)
 /* Unused for x86.  */
 # define INIT_ARCH()