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; }