title "Processor type and stepping detection" ;++ ; ; Copyright (c) 1989 Microsoft Corporation ; ; Module Name: ; ; cpu.asm ; ; Abstract: ; ; This module implements the assembley code necessary to determine ; cpu type and stepping information. ; ; Author: ; ; Shie-Lin Tzong (shielint) 28-Oct-1991. ; Some of the code is extracted from Cruiser (mainly, ; the code to determine 386 stepping.) ; ; Environment: ; ; 80x86 ; ; Revision History: ; ;-- .xlist include i386\cpu.inc include ks386.inc include callconv.inc include mac386.inc .list ; ; constant for i386 32-bit multiplication test ; MULTIPLIER equ 00000081h MULTIPLICAND equ 0417a000h RESULT_HIGH equ 00000002h RESULT_LOW equ 0fe7a000h ; ; Constants for Floating Point test ; REALLONG_LOW equ 00000000 REALLONG_HIGH equ 3FE00000h PSEUDO_DENORMAL_LOW equ 00000000h PSEUDO_DENORMAL_MID equ 80000000h PSEUDO_DENORMAL_HIGH equ 0000h .586p INIT SEGMENT DWORD PUBLIC 'CODE' ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING ;++ ; ; USHORT ; KiSetProcessorType ( ; VOID ; ) ; ; Routine Description: ; ; This function determines type of processor (80486, 80386), ; and it's corrisponding stepping. The results are saved in ; the current processor's PRCB. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Prcb->CpuType ; 3, 4, 5, ... 3 = 386, 4 = 486, etc.. ; ; Prcb->CpuStep is encoded as follows: ; lower byte as stepping # ; upper byte as stepping letter (0=a, 1=b, 2=c, ...) ; ; (ax) = x86h or 0 if unrecongnized processor. ; ;-- cPublicProc _KiSetProcessorType,0 mov byte ptr fs:PcPrcbData.PbCpuID, 0 push edi push esi push ebx ; Save C registers mov eax, cr0 push eax pushfd ; save Cr0 & flags pop ebx ; Get flags into eax push ebx ; Save original flags mov ecx, ebx xor ecx, EFLAGS_ID ; flip ID bit push ecx popfd ; load it into flags pushfd ; re-save flags pop ecx ; get flags into eax cmp ebx, ecx ; did bit stay flipped? jne short cpu_has_cpuid ; Yes, go use CPUID cpuid_unsupported: pop ebx ; Get flags into eax push ebx ; Save original flags mov ecx, ebx xor ecx, EFLAGS_AC ; flip AC bit push ecx popfd ; load it into flags pushfd ; re-save flags pop ecx ; get flags into eax cmp ebx, ecx ; did bit stay flipped? je short cpu_is_386 ; No, then this is a 386 cpu_is_486: mov byte ptr fs:PcPrcbData.PbCpuType, 4h ; Save CPU Type call Get486Stepping jmp cpu_save_stepping cpu_is_386: mov byte ptr fs:PcPrcbData.PbCpuType, 3h ; Save CPU Type call Get386Stepping jmp cpu_save_stepping cpu_has_cpuid: or ebx, EFLAGS_ID push ebx popfd ; Make sure ID bit is set mov ecx, fs:PcIdt ; Address of IDT push dword ptr [ecx+30h] ; Save Trap06 handler incase push dword ptr [ecx+34h] ; the CPUID instruction faults mov eax, offset CpuIdTrap6Handler mov word ptr [ecx+30h], ax ; Set LowWord shr eax, 16 mov word ptr [ecx+36h], ax ; Set HighWord mov eax, 0 ; argument to CPUID cpuid ; Uses eax, ebx, ecx, edx mov ecx, fs:PcIdt ; Address of IDT pop dword ptr [ecx+34h] ; restore trap6 handler pop dword ptr [ecx+30h] cmp eax, 1 ; make sure level 1 is supported jc short cpuid_unsupported ; no, then punt ; Get the family and stepping (cpuid fn=1). If processor family ; is less than 0xf, the format returned is as below: ; 3 2 1 ; 10987654321098765432109876543210 ; -------------------------------- ; ppffffmmmmssss ; where ; pp = Processor Type ; ffff = Family ; mmmm = Model ; ssss = Stepping ; ; This is transformed and saved in the PRCB as ; ; PRCB->CpuStep = 0000mmmm0000ssss v v ; ->CpuID = 00000001 | | v ; ->CpuType = 0000ffff | | | v ; | | | | ; ie the dword that contains all this looks like 0M0S010F ; ; If the processor family is 0xf or greater, the format returned is: ; 3 2 1 ; 10987654321098765432109876543210 ; -------------------------------- ; RRRRFFFFFFFFMMMMRRppffffmmmmssss ; where ; pp = Processor Type ; ffff = Family ; mmmm = Model ; ssss = Stepping ; MMMM = Extended Model ; FFFFFFFF = Extended Family ; RRRR, RR = Reserved ; This is transformed and saved in the PRCB as ; ; PRCB->CpuStep = EEEEEEEE0000ssss v v ; ->CpuID = 00000001 | | v ; ->CpuType = XXXXXXXX | | | v ; | | | | ; ie the dword that contains all this looks like EE0S01XX ; ; where ; EEEEEEEE = ((MMMM) << 4)8 bits + (mmmm)zero extended 8 bits ; XXXXXXXX = (FFFFFFFF) + (ffff)zero extended 8 bits ; The value for Extended Family cannot go beyond F0H inorder to support ; a maximum value of FFH for the final Family value(XXXXXXXX). ; The maximum value of Extended Model is FH and the maximum value for ; the final Model value(EEEEEEEE) is FFH mov eax, 1 ; get the family and stepping cpuid mov ebx, eax mov edx, eax and edx, 0F00h ; get the Family cmp edx, 0F00h ; (edx) = 00000000000000000000ffff00000000 jne short cpu_not_extended ; Family less than F mov ah, al ; (eax) = RRRRFFFFFFFFMMMMmmmmssssmmmmssss shr eax, 4 ; (eax) = 0000RRRRFFFFFFFFMMMMmmmmssssmmmm mov al, bl ; (eax) = 0000RRRRFFFFFFFFMMMMmmmmmmmmssss and eax, 0FF0Fh ; (eax) = 0000000000000000EEEEEEEE0000ssss and ebx, 0FF00000h ; (ebx) = 0000FFFFFFFF00000000000000000000 shr ebx, 12 ; (ebx) = 0000000000000000FFFFFFFF00000000 add ebx, edx ; (ebx) = 0000000000000000XXXXXXXX00000000 jmp short cpu_save_signature cpu_not_extended: and eax, 0F0h ; (eax) = Model shl eax, 4 mov al, bl and eax, 0F0Fh ; (eax) = Model[15:8] | Step[7:0] and ebx, 0F00h ; (bh) = CpuType cpu_save_signature: mov byte ptr fs:PcPrcbData.PbCpuID, 1 ; Has ID support mov byte ptr fs:PcPrcbData.PbCpuType, bh ; Save CPU Type cpu_save_stepping: mov word ptr fs:PcPrcbData.PbCpuStep, ax ; Save CPU Stepping popfd ; Restore flags pop eax mov cr0, eax pop ebx pop esi pop edi stdRET _KiSetProcessorType cpuid_trap: mov ecx, fs:PcIdt ; Address of IDT pop dword ptr [ecx+34h] ; restore trap6 handler pop dword ptr [ecx+30h] jmp cpuid_unsupported ; Go get processor information stdENDP _KiSetProcessorType ;++ ; ; BOOLEAN ; CpuIdTrap6 ( ; VOID ; ) ; ; Routine Description: ; ; Temporary int 6 handler - assumes the cause of the exception was the ; attempted CPUID instruction. ; ; Arguments: ; ; None. ; ; Return Value: ; ; none. ; ;-- CpuIdTrap6Handler proc mov [esp].IretEip,offset cpuid_trap iretd CpuIdTrap6Handler endp ;++ ; ; USHORT ; Get386Stepping ( ; VOID ; ) ; ; Routine Description: ; ; This function determines cpu stepping for i386 CPU stepping. ; ; Arguments: ; ; None. ; ; Return Value: ; ; [ax] - Cpu stepping. ; 0 = A, 1 = B, 2 = C, ... ; ;-- public Get386Stepping Get386Stepping proc call MultiplyTest ; Perform mutiplication test jnc short G3s00 ; if nc, muttest is ok mov ax, 0 ret G3s00: call Check386B0 ; Check for B0 stepping jnc short G3s05 ; if nc, it's B1/later mov ax, 100h ; It is B0/earlier stepping ret G3s05: call Check386D1 ; Check for D1 stepping jc short G3s10 ; if c, it is NOT D1 mov ax, 301h ; It is D1/later stepping ret G3s10: mov ax, 101h ; assume it is B1 stepping ret Get386Stepping endp ;++ ; ; USHORT ; Get486Stepping ( ; VOID ; ) ; ; Routine Description: ; ; This function determines cpu stepping for i486 CPU type. ; ; Arguments: ; ; None. ; ; Return Value: ; ; [ax] - Cpu stepping. For example, [ax] = D0h for D0 stepping. ; ;-- public Get486Stepping Get486Stepping proc call Check486AStepping ; Check for A stepping jnc short G4s00 ; if nc, it is NOT A stepping mov ax, 0 ; set to A stepping ret G4s00: call Check486BStepping ; Check for B stepping jnc short G4s10 ; if nc, it is NOT a B stepping mov ax, 100h ; set to B stepping ret ; ; Before we test for 486 C/D step, we need to make sure NPX is present. ; Because the test uses FP instruction to do the detection. ; G4s10: call _KiIsNpxPresent ; Check if cpu has coprocessor support? or ax, ax jz short G4s15 ; it is actually 486sx call Check486CStepping ; Check for C stepping jnc short G4s20 ; if nc, it is NOT a C stepping G4s15: mov ax, 200h ; set to C stepping ret G4s20: mov ax, 300h ; Set to D stepping ret Get486Stepping endp ;++ ; ; BOOLEAN ; Check486AStepping ( ; VOID ; ) ; ; Routine Description: ; ; This routine checks for 486 A Stepping. ; ; It takes advantage of the fact that on the A-step of the i486 ; processor, the ET bit in CR0 could be set or cleared by software, ; but was not used by the hardware. On B or C -step, ET bit in CR0 ; is now hardwired to a "1" to force usage of the 386 math coprocessor ; protocol. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear if B or later stepping. ; Carry Flag set if A or earlier stepping. ; ;-- public Check486AStepping Check486AStepping proc near mov eax, cr0 ; reset ET bit in cr0 and eax, NOT CR0_ET mov cr0, eax mov eax, cr0 ; get cr0 back test eax, CR0_ET ; if ET bit still set? jnz short cas10 ; if nz, yes, still set, it's NOT A step stc ret cas10: clc ret Check486AStepping endp ;++ ; ; BOOLEAN ; Check486BStepping ( ; VOID ; ) ; ; Routine Description: ; ; This routine checks for 486 B Stepping. ; ; On the i486 processor, the "mov to/from DR4/5" instructions were ; aliased to "mov to/from DR6/7" instructions. However, the i486 ; B or earlier steps generate an Invalid opcode exception when DR4/5 ; are used with "mov to/from special register" instruction. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear if C or later stepping. ; Carry Flag set if B stepping. ; ;-- public Check486BStepping Check486BStepping proc push ebx mov ebx, fs:PcIdt ; Address of IDT push dword ptr [ebx+30h] push dword ptr [ebx+34h] ; Save Trap06 handler mov eax, offset Temporary486Int6 mov word ptr [ebx+30h], ax ; Set LowWord shr eax, 16 mov word ptr [ebx+36h], ax ; Set HighWord c4bs50: db 0fh, 21h, 0e0h ; mov eax, DR4 nop nop nop nop nop clc ; it is C step jmp short c4bs70 c4bs60: stc ; it's B step c4bs70: pop dword ptr [ebx+34h] ; restore old int 6 vector pop dword ptr [ebx+30h] pop ebx ret ret Check486BStepping endp ;++ ; ; BOOLEAN ; Temporary486Int6 ( ; VOID ; ) ; ; Routine Description: ; ; Temporary int 6 handler - assumes the cause of the exception was the ; attempted execution of an mov to/from DR4/5 instruction. ; ; Arguments: ; ; None. ; ; Return Value: ; ; none. ; ;-- Temporary486Int6 proc mov [esp].IretEIp,offset c4bs60 ; set EIP to stc instruction iretd Temporary486Int6 endp ;++ ; ; BOOLEAN ; Check486CStepping ( ; VOID ; ) ; ; Routine Description: ; ; This routine checks for 486 C Stepping. ; ; This routine takes advantage of the fact that FSCALE produces ; wrong result with Denormal or Pseudo-denormal operand on 486 ; C and earlier steps. ; ; If the value contained in ST(1), second location in the floating ; point stack, is between 1 and 11, and the value in ST, top of the ; floating point stack, is either a pseudo-denormal number or a ; denormal number with the underflow exception unmasked, the FSCALE ; instruction produces an incorrect result. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear if D or later stepping. ; Carry Flag set if C stepping. ; ;-- FpControl equ [ebp - 2] RealLongSt1 equ [ebp - 10] PseudoDenormal equ [ebp - 20] FscaleResult equ [ebp - 30] public Check486CStepping Check486CStepping proc push ebp mov ebp, esp sub esp, 30 ; Allocate space for temp real variables mov eax, cr0 ; Don't trap while doing math and eax, NOT (CR0_ET+CR0_MP+CR0_TS+CR0_EM) mov cr0, eax ; ; Initialize the local FP variables to predefined values. ; RealLongSt1 = 1.0 * (2 ** -1) = 0.5 in normalized double precision FP form ; PseudoDenormal = a unsupported format by IEEE. ; Sign bit = 0 ; Exponent = 000000000000000B ; Significand = 100000...0B ; FscaleResult = The result of FSCALE instruction. Depending on 486 step, ; the value will be different: ; Under C and earlier steps, 486 returns the original value ; in ST as the result. The correct returned value should be ; original significand and an exponent of 0...01. ; mov dword ptr RealLongSt1, REALLONG_LOW mov dword ptr RealLongSt1 + 4, REALLONG_HIGH mov dword ptr PseudoDenormal, PSEUDO_DENORMAL_LOW mov dword ptr PseudoDenormal + 4, PSEUDO_DENORMAL_MID mov word ptr PseudoDenormal + 8, PSEUDO_DENORMAL_HIGH .387 fnstcw FpControl ; Get FP control word fwait or word ptr FpControl, 0FFh ; Mask all the FP exceptions fldcw FpControl ; Set FP control fld qword ptr RealLongSt1 ; 0 < ST(1) = RealLongSt1 < 1 fld tbyte ptr PseudoDenormal; Denormalized operand. Note, i486 ; won't report denormal exception ; on 'FLD' instruction. ; ST(0) = Extended Denormalized operand fscale ; try to trigger 486Cx errata fstp tbyte ptr FscaleResult ; Store ST(0) in FscaleResult cmp word ptr FscaleResult + 8, PSEUDO_DENORMAL_HIGH ; Is Exponent changed? jz short c4ds00 ; if z, no, it is C step clc jmp short c4ds10 c4ds00: stc c4ds10: mov esp, ebp pop ebp ret Check486CStepping endp ;++ ; ; BOOLEAN ; Check386B0 ( ; VOID ; ) ; ; Routine Description: ; ; This routine checks for 386 B0 or earlier stepping. ; ; It takes advantage of the fact that the bit INSERT and ; EXTRACT instructions that existed in B0 and earlier versions of the ; 386 were removed in the B1 stepping. When executed on the B1, INSERT ; and EXTRACT cause an int 6 (invalid opcode) exception. This routine ; can therefore discriminate between B1/later 386s and B0/earlier 386s. ; It is intended to be used in sequence with other checks to determine ; processor stepping by exercising specific bugs found in specific ; steppings of the 386. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear if B1 or later stepping ; Carry Flag set if B0 or prior ; ;-- Check386B0 proc push ebx mov ebx, fs:PcIdt ; Address of IDT push dword ptr [ebx+30h] push dword ptr [ebx+34h] ; Save Trap06 handler mov eax, offset TemporaryInt6 mov word ptr [ebx+30h], ax ; Set LowWord shr eax, 16 mov word ptr [ebx+36h], ax ; Set HighWord ; ; Attempt execution of Extract Bit String instruction. Execution on ; B0 or earlier with length (CL) = 0 will return 0 into the destination ; (CX in this case). Execution on B1 or later will fail either due to ; taking the invalid opcode trap, or if the opcode is valid, we don't ; expect CX will be zeroed by any new instruction supported by newer ; steppings. The dummy int 6 handler will clears the Carry Flag and ; returns execution to the appropriate label. If the instruction ; actually executes, CX will *probably* remain unchanged in any new ; stepping that uses the opcode for something else. The nops are meant ; to handle newer steppings with an unknown instruction length. ; xor eax,eax mov edx,eax mov ecx,0ff00h ; Extract length (CL) == 0, (CX) != 0 b1c50: db 0fh, 0a6h, 0cah ; xbts cx,dx,ax,cl nop nop nop nop nop stc ; assume B0 jecxz short b1c70 ; jmp if B0 b1c60: clc b1c70: pop dword ptr [ebx+34h] ; restore old int 6 vector pop dword ptr [ebx+30h] pop ebx ret Check386B0 endp ;++ ; ; BOOLEAN ; TemporaryInt6 ( ; VOID ; ) ; ; Routine Description: ; ; Temporary int 6 handler - assumes the cause of the exception was the ; attempted execution of an XTBS instruction. ; ; Arguments: ; ; None. ; ; Return Value: ; ; none. ; ;-- TemporaryInt6 proc mov [esp].IretEip,offset b1c60 ; set IP to clc instruction iretd TemporaryInt6 endp ;++ ; ; BOOLEAN ; Check386D1 ( ; VOID ; ) ; ; Routine Description: ; ; This routine checks for 386 D1 Stepping. ; ; It takes advantage of the fact that on pre-D1 386, if a REPeated ; MOVS instruction is executed when single-stepping is enabled, ; a single step trap is taken every TWO moves steps, but should ; occuu each move step. ; ; NOTE: This routine cannot distinguish between a D0 stepping and a D1 ; stepping. If a need arises to make this distinction, this routine ; will need modification. D0 steppings will be recognized as D1. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear if D1 or later stepping ; Carry Flag set if B1 or prior ; ;-- Check386D1 proc push ebx mov ebx, fs:PcIdt ; Address of IDT push dword ptr [ebx+08h] push dword ptr [ebx+0ch] ; Save Trap01 handler mov eax, offset TemporaryInt1 mov word ptr [ebx+08h], ax ; Set LowWord shr eax, 16 mov word ptr [ebx+0eh], ax ; Set HighWord ; ; Attempt execution of rep movsb instruction with the Trace Flag set. ; Execution on B1 or earlier with length (CX) > 1 will trace over two ; iterations before accepting the trace trap. Execution on D1 or later ; will accept the trace trap after a single iteration. The dummy int 1 ; handler will return execution to the instruction following the movsb ; instruction. Examination of (CX) will reveal the stepping. ; sub esp,4 ; make room for target of movsb mov esi, offset TemporaryInt1 ; (ds:esi) -> some present data mov edi,esp mov ecx,2 ; 2 iterations pushfd or dword ptr [esp], EFLAGS_TF popfd ; cause a single step trap rep movsb d1c60: add esp,4 ; clean off stack pop dword ptr [ebx+0ch] ; restore old int 1 vector pop dword ptr [ebx+08h] stc ; assume B1 jecxz short d1cx ; jmp if <= B1 clc ; else clear carry to indicate >= D1 d1cx: pop ebx ret Check386D1 endp ;++ ; ; BOOLEAN ; TemporaryInt1 ( ; VOID ; ) ; ; Routine Description: ; ; Temporary int 1 handler - assumes the cause of the exception was ; trace trap at the above rep movs instruction. ; ; Arguments: ; ; (esp)->eip of trapped instruction ; cs of trapped instruction ; eflags of trapped instruction ; ;-- TemporaryInt1 proc and [esp].IretEFlags,not EFLAGS_TF ; clear caller's Trace Flag mov [esp].IretEip,offset d1c60 ; set IP to next instruction iretd TemporaryInt1 endp ;++ ; ; BOOLEAN ; MultiplyTest ( ; VOID ; ) ; ; Routine Description: ; ; This routine checks the 386 32-bit multiply instruction. ; The reason for this check is because some of the i386 fail to ; perform this instruction. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear on success ; Carry Flag set on failure ; ;-- ; MultiplyTest proc xor cx,cx ; 64K times is a nice round number mlt00: push cx call Multiply ; does this chip's multiply work? pop cx jc short mltx ; if c, No, exit loop mlt00 ; if nc, YEs, loop to try again clc mltx: ret MultiplyTest endp ;++ ; ; BOOLEAN ; Multiply ( ; VOID ; ) ; ; Routine Description: ; ; This routine performs 32-bit multiplication test which is known to ; fail on bad 386s. ; ; Note, the supplied pattern values must be used for consistent results. ; ; Arguments: ; ; None. ; ; Return Value: ; ; Carry Flag clear on success. ; Carry Flag set on failure. ; ;-- Multiply proc mov ecx, MULTIPLIER mov eax, MULTIPLICAND mul ecx cmp edx, RESULT_HIGH ; Q: high order answer OK ? stc ; assume failure jnz short mlpx ; N: exit with error cmp eax, RESULT_LOW ; Q: low order answer OK ? stc ; assume failure jnz short mlpx ; N: exit with error clc ; indicate success mlpx: ret Multiply endp ;++ ; ; BOOLEAN ; KiIsNpxPresent( ; VOID ; ); ; ; Routine Description: ; ; This routine determines if there is any Numeric coprocessor ; present. ; ; Note that we do NOT determine its type (287, 387). ; This code is extracted from Intel book. ; ; Arguments: ; ; None. ; ; Return: ; ; TRUE - If NPX is present. Else a value of FALSE is returned. ; Sets CR0 NPX bits accordingly. ; ;-- cPublicProc _KiIsNpxPresent,0 push ebp ; Save caller's bp mov eax, cr0 and eax, NOT (CR0_ET+CR0_MP+CR0_TS+CR0_EM) mov cr0, eax xor edx, edx .287 fninit ; Initialize NPX mov ecx, 5A5A5A5Ah ; Put non-zero value push ecx ; into the memory we are going to use mov ebp, esp fnstsw word ptr [ebp] ; Retrieve status - must use non-wait cmp byte ptr [ebp], 0 ; All bits cleared by fninit? jne Inp10 or eax, CR0_ET mov edx, 1 cmp fs:PcPrcbData.PbCpuType, 3h jbe Inp10 or eax, CR0_NE Inp10: or eax, CR0_EM+CR0_TS ; During Kernel Initialization set ; the EM bit mov cr0, eax pop eax ; clear scratch value pop ebp ; Restore caller's bp mov eax, edx stdRet _KiIsNpxPresent stdENDP _KiIsNpxPresent ;++ ; ; VOID ; CPUID ( ; ULONG InEax, ; PULONG OutEax, ; PULONG OutEbx, ; PULONG OutEcx, ; PULONG OutEdx ; ); ; ; Routine Description: ; ; Executes the CPUID instruction and returns the registers from it ; ; Only available at INIT time ; ; Arguments: ; ; Return Value: ; ;-- cPublicProc _CPUID,5 push ebx push esi mov eax, [esp+12] cpuid mov esi, [esp+16] ; return EAX mov [esi], eax mov esi, [esp+20] ; return EBX mov [esi], ebx mov esi, [esp+24] ; return ECX mov [esi], ecx mov esi, [esp+28] ; return EDX mov [esi], edx pop esi pop ebx stdRET _CPUID stdENDP _CPUID ;++ ; ; LONGLONG ; RDTSC ( ; VOID ; ); ; ; Routine Description: ; ; Arguments: ; ; Return Value: ; ;-- cPublicProc _RDTSC rdtsc stdRET _RDTSC stdENDP _RDTSC INIT ENDS _TEXT SEGMENT DWORD PUBLIC 'CODE' ; Put IdleLoop in text section ASSUME DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING ;++ ; ; ULONGLONG ; FASTCALL ; RDMSR ( ; IN ULONG MsrRegister ; ); ; ; Routine Description: ; ; Arguments: ; ; Return Value: ; ;-- cPublicFastCall RDMSR, 1 rdmsr fstRET RDMSR fstENDP RDMSR ;++ ; ; VOID ; WRMSR ( ; IN ULONG MsrRegister ; IN LONGLONG MsrValue ; ); ; ; Routine Description: ; ; Arguments: ; ; Return Value: ; ;-- cPublicProc _WRMSR, 3 mov ecx, [esp+4] mov eax, [esp+8] mov edx, [esp+12] wrmsr stdRET _WRMSR stdENDP _WRMSR ;++ ; ; VOID ; KeYieldProcessor ( ; VOID ; ); ; ; Routine Description: ; ; Yields a thread of the processor ; ; Arguments: ; ; Return Value: ; ;-- cPublicProc _KeYieldProcessor YIELD stdRET _KeYieldProcessor stdENDP _KeYieldProcessor _TEXT ENDS END