CPUID Patch for IDT Winchip

tedheadster tedheadster@gmail.com
Sun May 19 16:11:00 GMT 2019


I have confirmed that the IDT Winchip 2 does not expressly set %ecx
after a call to cpuid() with %eax = 1, and this causes incorrect
reporting of cpu capabilities. The %ecx register should return 0x0
(and likely %ebx should too) on this hardware. This patch proposes a
fix.

The symptoms of this arose on a Winchip 2 system when systemd called
cpuid() with %eax = 1 and it incorrectly reported rdrand() support
(bit 30 set in %ecx). A call to the supposedly supported rdrand()
function triggered an illegal instruction exception, causing the
system to panic early in booting.

The IDT Winchip documentation is silent on what happens to %ecx and
%ebx after such a call (see below). It appears to leave whatever is in
%ecx untouched instead of zeroing it. This patch defensively zeroes
out %ebx:%ecx:%edx before such a call in cpuid.h.

I should be able to test the earlier Winchip C6 model in the future. I
wlll also check older AMD, Cyrix, and VIA processors.

Thanks to Mike Gilbert for suggesting the solution. Thank you also to
Segher Boessenkool for helping get the inline assembly syntax correct.

Here is the assembly code fragment the patch generates with comments:

   0xb7e21498 <+88>:    xor    %edi,%edi <--- zeroes %edi for use below
   0xb7e2149a <+90>:    mov    %edi,%eax
   0xb7e2149c <+92>:    cpuid
   0xb7e2149e <+94>:    test   %eax,%eax
   0xb7e214a0 <+96>:    je     0xb7e214c0 <rdrand+128>
   0xb7e214a2 <+98>:    mov    %edi,%ebx <--- zeroes %ebx
   0xb7e214a4 <+100>:   mov    %edi,%ecx <--- zeroes %ecx
   0xb7e214a6 <+102>:   mov    %edi,%edx <--- zeroes %edx
   0xb7e214a8 <+104>:   mov    $0x1,%eax <--- call cpuid with %eax = 1
   0xb7e214ad <+109>:   cpuid
   0xb7e214af <+111>:   shr    $0x1e

References:

http://datasheets.chipdb.org/IDT/x86/WinChip2/wc2ds.pdf
http://datasheets.chipdb.org/IDT/x86/C6/c6_data_sheet.pdf

- Matthew Whitehead

diff --git a/gcc/config/i386/cpuid.h b/gcc/config/i386/cpuid.h
index b3b0f91..7f6d726 100644
--- a/gcc/config/i386/cpuid.h
+++ b/gcc/config/i386/cpuid.h
@@ -251,6 +251,15 @@ __get_cpuid (unsigned int __leaf,
   if (__maxlevel == 0 || __maxlevel < __leaf)
     return 0;

+  /* At least one confirmed cpu (Winchip 2) does not set %ecx correctly for
+    cpuid() %eax = 1, and leaves garbage in it (usually containing the results
+    of a previous call to cpuid() %eax = 0, which puts 'auls' in %ecx from
+    'CentaurHauls' in %ebx:%edx:%ecx, the vendor identification string).
+    Forcibly zero the three registers before calling cpuid() as a
precaution. */
+
+  *__ebx = *__ecx = *__edx = 0;
+  asm volatile("" : "+b" (*__ebx), "+c" (*__ecx), "+d" (*__edx));
+
   __cpuid (__leaf, *__eax, *__ebx, *__ecx, *__edx);
   return 1;
 }



More information about the Gcc-patches mailing list