/// -*- mode: asm; asm-comment-char: ?/ -*- /// /// CPU dispatch support for x86 /// /// (c) 2019 Straylight/Edgeware /// ///----- Licensing notice --------------------------------------------------- /// /// This file is part of Catacomb. /// /// Catacomb is free software: you can redistribute it and/or modify it /// under the terms of the GNU Library General Public License as published /// by the Free Software Foundation; either version 2 of the License, or /// (at your option) any later version. /// /// Catacomb 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 /// Library General Public License for more details. /// /// You should have received a copy of the GNU Library General Public /// License along with Catacomb. If not, write to the Free Software /// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, /// USA. ///-------------------------------------------------------------------------- /// Preliminaries. #include "config.h" #include "asm-common.h" EFLAGS_ID = 1 << 21 .text ///-------------------------------------------------------------------------- /// Probing for CPUID. FUNC(dispatch_x86ish_cpuid) // Enter with a pointer to 16 bytes of storage for the output A, B, // C, D values in the first argument, and input A and C values in the // second and third. Fill the output buffer with `cpuid' results and // return zero if we can; otherwise fill with zero and return -1. #if CPUFAM_X86 # define OUT edi pushreg ebx pushreg edi mov OUT, [SP + 12] mov eax, [SP + 16] mov ecx, [SP + 20] #endif #if CPUFAM_AMD64 && ABI_SYSV # define OUT rdi pushreg rbx mov eax, esi mov ecx, edx #endif #if CPUFAM_AMD64 && ABI_WIN # define OUT r9 pushreg rbx mov OUT, rcx mov eax, edx mov ecx, r8d #endif endprologue // First, check that this is even a thing, using the complicated // dance with the flags register. This is unnecessary on AMD64, // which postdates the introduction of `cpuid'. #if CPUFAM_X86 pushf pop DX // current flags in d or DX, EFLAGS_ID // force the id bit on and check it push DX popf pushf pop DX test edx, EFLAGS_ID jz 8f and DX, ~EFLAGS_ID // force the id bit off and check it push DX popf pushf pop DX test edx, EFLAGS_ID jnz 8f #endif // OK, that seemed to work. cpuid mov [OUT + 0], eax mov [OUT + 4], ebx mov [OUT + 8], ecx mov [OUT + 12], edx xor eax, eax // We're done. 9: #if CPUFAM_X86 popreg edi popreg ebx #endif #if CPUFAM_AMD64 popreg rbx #endif ret // Failed. #if CPUFAM_X86 8: xor eax, eax mov [OUT + 0], eax mov [OUT + 4], eax mov [OUT + 8], eax mov [OUT + 12], eax mov eax, -1 jmp 9b #endif #undef OUT ENDFUNC ///-------------------------------------------------------------------------- /// Probing for XMM register availability. FUNC(dispatch_x86ish_xmmregisters_p) // Enter with no arguments. Return nonzero if the XMM registers are // usable. pushreg BP setfp stalloc 512 and SP, ~15 endprologue // Save the floating point and SIMD registers, and try to clobber // xmm0. lea DX, [SP + 160] fxsave [SP] mov eax, [DX] xor dword ptr [DX], 0xaaaa5555 fxrstor [SP] // Save them again, and read back the low word of xmm0. Undo the // clobbering and restore. fxsave [SP] mov ecx, [DX] mov [DX], eax fxrstor [SP] // The register are live if we read different things. xor eax, ecx // Done. dropfp popreg BP ret ENDFUNC ///-------------------------------------------------------------------------- /// Checking extended control registers. FUNC(dispatch_x86ish_xgetbv) // Call with two arguments: a pointer Z_OUT to 8 bytes of output space, and // a 32-bit integer C. Read the 64-bit value of XCR(C), and store it // at Z_OUT. #if CPUFAM_X86 # define Z_OUT edi pushreg edi mov Z_OUT, [esp + 8] mov ecx, [esp + 12] #endif #if CPUFAM_AMD64 && ABI_SYSV # define Z_OUT rdi mov ecx, esi #endif #if CPUFAM_AMD64 && ABI_WIN # define Z_OUT r8 mov r8, rcx mov ecx, edx #endif endprologue xgetbv mov [Z_OUT + 0], eax mov [Z_OUT + 4], edx #if CPUFAM_X86 popreg edi #endif ret #undef Z_OUT ENDFUNC ///-------------------------------------------------------------------------- /// Checking `rdrand'. FUNC(dispatch_x86ish_rdrand) // Enter with two arguments: a code OP requesting either `rdrand' (0) // or `rdseed' (1), and a pointer X_OUT to a 32-bit word. Try to // generate a random word using the requested instruction'. If // successful, set *X_OUT to the generated word, and return zero; // otherwise, return -1. #if CPUFAM_X86 # define OP eax # define X_OUT edx # define COUNT ecx mov OP, [SP + 4] mov X_OUT, [SP + 8] #endif #if CPUFAM_AMD64 && ABI_SYSV # define OP edi # define X_OUT rsi # define COUNT ecx #endif #if CPUFAM_AMD64 && ABI_WIN # define OP rcx # define X_OUT rdx # define COUNT r8d #endif endprologue cmp OP, 0 mov COUNT, 16 // fairly persistent jne 1f 0: rdrand eax jc 9f dec COUNT jnz 0b jmp 8f 1: rdseed eax jc 9f dec COUNT jnz 1b jmp 8f // Failed to come up with a random value. 8: mov eax, -1 ret // Success. 9: mov [X_OUT], eax xor eax, eax ret #undef X_OUT #undef COUNT ENDFUNC ///----- That's all, folks --------------------------------------------------