aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2024-05-28 13:12:16 +0200
committerFlorian Weimer <fweimer@redhat.com>2024-05-30 14:33:36 +0200
commit11db7b454a9cb72ce14be5498cc3f137e9026291 (patch)
tree808446f47964d0182bee0bd23db8b60ccc3e6f92
parent815c04826929e41e350f9e1f8e0d297725d14064 (diff)
downloadglibc-11db7b454a9cb72ce14be5498cc3f137e9026291.zip
glibc-11db7b454a9cb72ce14be5498cc3f137e9026291.tar.gz
glibc-11db7b454a9cb72ce14be5498cc3f137e9026291.tar.bz2
WIP CPUID consistency checks
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/Makefile6
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c227
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S274
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py34
-rw-r--r--sysdeps/x86/Makefile7
-rw-r--r--sysdeps/x86/dl-diagnostics-cpu.c19
-rw-r--r--sysdeps/x86/dl-get-cpu-features.c28
-rw-r--r--sysdeps/x86/dl-x86_cpu_feature_diagnostics.c26
-rw-r--r--sysdeps/x86/include/cpu-features.h23
9 files changed, 632 insertions, 12 deletions
diff --git a/sysdeps/unix/sysv/linux/x86_64/Makefile b/sysdeps/unix/sysv/linux/x86_64/Makefile
index fcbffd8..4fb0dd4 100644
--- a/sysdeps/unix/sysv/linux/x86_64/Makefile
+++ b/sysdeps/unix/sysv/linux/x86_64/Makefile
@@ -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
index 0000000..6486ebb
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/dl-x86_cpu_feature_diagnostics.c
@@ -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
index 0000000..e122676
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/dl-x86_probes.S
@@ -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
index 0000000..a8730d3
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/x86_64/manual-x86_cpu_feature-diagnostics.py
@@ -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
diff --git a/sysdeps/x86/Makefile b/sysdeps/x86/Makefile
index 5311b59..010f15b 100644
--- a/sysdeps/x86/Makefile
+++ b/sysdeps/x86/Makefile
@@ -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 += \
diff --git a/sysdeps/x86/dl-diagnostics-cpu.c b/sysdeps/x86/dl-diagnostics-cpu.c
index ceafde9..5555c47 100644
--- a/sysdeps/x86/dl-diagnostics-cpu.c
+++ b/sysdeps/x86/dl-diagnostics-cpu.c
@@ -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 ();
}
diff --git a/sysdeps/x86/dl-get-cpu-features.c b/sysdeps/x86/dl-get-cpu-features.c
index f36d42d..7961aa3 100644
--- a/sysdeps/x86/dl-get-cpu-features.c
+++ b/sysdeps/x86/dl-get-cpu-features.c
@@ -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
index 0000000..bb7ff9c
--- /dev/null
+++ b/sysdeps/x86/dl-x86_cpu_feature_diagnostics.c
@@ -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)
+{
+}
diff --git a/sysdeps/x86/include/cpu-features.h b/sysdeps/x86/include/cpu-features.h
index cd7bd27..2eea95d 100644
--- a/sysdeps/x86/include/cpu-features.h
+++ b/sysdeps/x86/include/cpu-features.h
@@ -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()