This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] Common cpuid wrappers, use SYS_cpuid when available


Currently there is no way of disabling CPU features reported by the CPUID
instruction. Which sometimes turn out to be broken [1] or undesired [2].
We can assume we will run into similar situations again sooner or later.
The only way to fix this is to do a microcode update (if it is available),
as the BIOS does not provide a way to disable CPUID bits either. When there is
no new microcode, then there is no way to tell your system not to use certain
CPU features. This sometimes leads to an unbootable and/or unusable system.
Plus the ability to quickly disable certain CPU extensions would be handy for
debugging.

This patch aims at providing system-wide support for the kernel-adjusted CPUID:
* The kernel takes a command line parameter (cpu-=...) allowing for an easy way
  to disable any of the known CPUID capability bits [3]. Plus the kernel may
  disable certain features by itself as well.
* Then the kernel provides a system call for obtaining the adjusted data [4]
  (SYS_cpuid, to be used instead of the __cpuid* macros from GCC's cpuid.h).

Since the cpuid instruction is available from the user-space, use of SYS_cpuid
cannot be enforced on programmers. But it can be encouraged, and making glibc
respect it is a good start (and a requirement for this purpose).
The expected impact is, after the new versions of kernel and glibc are widely
adopted, to discourage use of low-level __cpuid* macros for checking supported
CPU features on Linux as a coding issue that workarounds and breaks system
features.
And we may expect users to report bugs for programs that do not respect features
being disabled. Especially that they will be trivial to fix.
It will take time, but if this is introduced now, it may become a widely used
solution in a few years that will finally allow us to easily disable unwanted
CPU features on demand.

This feature is NOT implemented in the Linux kernel yet.
However I would like to ask you to say if you *would* ACK this *if* the
SYS_cpuid system call were to be adopted into the kernel.
Obviously shipping either without the other does not make any sense.

This is also my very first patch for glibc, so please let me know of any code
quality issues or improvement suggestions.

On GitLab you can find trees with both this patch [5] and the latest Linux
kernel patched [6]. And I attach a test program for the SYS_cpuid below [7].

[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800574
[2] https://devtalk.nvidia.com/default/topic/893325/newest-and-beta-linux-driver-causing-segmentation-fault-core-dumped-on-all-skylake-platforms/
[3] e.g. 'linux ... nosplash quiet cpu-=mmx,sse,sse2'
[4] long sys_cpuid(const u32 level, const u32 count,
                   u32 __user *eax, u32 __user *ebx,
                   u32 __user *ecx, u32 __user *edx);
[5] https://gitlab.com/ultr/glibc/tags/ultr-sys_cpuid
[6] https://gitlab.com/ultr/linux/tags/ultr-sys_cpuid-master
[7] SYS_cpuid test program:
- - - - cut here - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#include <stdio.h>
#include <stdint.h>

#include <unistd.h>
#include <sys/syscall.h>

#include <cpuid.h>

#ifndef __linux__
    #warning Not a Linux!
#endif

#ifndef SYS_cpuid
    #warning Defining undefined SYS_cpuid!
    #ifdef __x86_64__
        #define SYS_cpuid 327
    #else
        #define SYS_cpuid 378
    #endif
#endif

void get_kernel(const uint32_t level, const uint32_t count) {
    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
    int ret = syscall(SYS_cpuid, level, count, &eax, &ebx, &ecx, &edx);
    printf("sys_cpuid==%d:\t[0x%08lX,%lu] => [0x%08lX,0x%08lX,0x%08lX,0x%08lX]\n", ret, level, count, eax, ebx, ecx, edx);
}

void get_native(const uint32_t level, const uint32_t count) {
    register uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
    __cpuid_count(level, count, eax, ebx, ecx, edx);
    printf("native cpuid:\t[0x%08lX,%lu] => [0x%08lX,0x%08lX,0x%08lX,0x%08lX]\n", level, count, eax, ebx, ecx, edx);
}

void get(const uint32_t level, const uint32_t count) {
    get_native(level, count);
    get_kernel(level, count);
}

int main(int argc, char **argv) {
    printf("SYS_cpuid = %d\n", SYS_cpuid);
    get(0x00000001, 0);
    get(0x00000006, 0);
    get(0x00000007, 0);
    get(0x0000000D, 1);
    get(0x0000000F, 0);
    get(0x0000000F, 1);
    get(0x80000001, 0);
    get(0x80000008, 0);
    get(0x8000000A, 0);
    get(0x80860001, 0);
    get(0xC0000001, 0);

    get(0x00000002, 0);
    get(0x00000004, 0);
    get(0x00000004, 1);
    get(0x00000004, 2);
    get(0x00000004, 3);
    return 0;
}
- - - - cut here - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Regards,
Piotr Henryk Dabrowski


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]