/*++ Copyright (c) 2001 Microsoft Corporation Module Name: npxstres.c Abstract: This test validates Npx state management and Npx exception handling. Author: Environment: User mode only. Revision History: --*/ #include "pch.h" int _cdecl main( int argc, char **argv ) /*++ Routine Description: Main function for npxstres.exe Arguments: argc - Argument count argv - Argument array Return Value: zero for success, nonzero for non low resource failures. --*/ { HANDLE *hThreadArray; DWORD dwThreadId, i, threadCount; TEST_INFO testInfo; FILE *handle; printf("Npxstres.exe \n (returns errmask, zero if none)\n\n"); if (argc > 1) { threadCount = atoi(argv[1]); } else { threadCount = DEF_NUM_THREADS; } hThreadArray = (HANDLE *) malloc(sizeof(HANDLE)*threadCount); if (hThreadArray == NULL) { printf("Insufficient memory to test\n"); return 0; } __try { InitializeCriticalSection(&testInfo.Crit); } __except(EXCEPTION_EXECUTE_HANDLER) { printf("Insufficient memory to test\n"); return 0; } printf("Running FPU tests using %d threads", threadCount); testInfo.FailureFlags = 0; DoFpPreinitTest(&testInfo, 0); for(i=0; iFailureFlags & FailureFlags) { fprintf(Handle, "FAILED\n"); } else { fprintf(Handle, "Pass\n"); } } VOID SetFailureFlag( IN OUT PTEST_INFO TestInfo, IN ULONG FailureFlags ) { EnterCriticalSection(&TestInfo->Crit); TestInfo->FailureFlags |= FailureFlags; LeaveCriticalSection(&TestInfo->Crit); } DWORD WINAPI FpThread( LPVOID Parameter ) { DWORD i; PTEST_INFO pTestInfo; pTestInfo = (PTEST_INFO) Parameter; printf("."); DoFpPreinitTest(pTestInfo, PREINIT_FLAG_CLEANTHREAD); printf("."); DoFpControlCorruptionTest(pTestInfo); printf("."); DoFpStatusCorruptionTest(pTestInfo); printf("."); DoFpSt0CorruptionTest(pTestInfo); printf("."); DoPendingExceptionTest(pTestInfo, EXCEPTIONTEST_FLAG_SPIN, FAILURECASE_SPIN_PEND); printf("."); DoPendingExceptionTest(pTestInfo, EXCEPTIONTEST_FLAG_SLEEP, FAILURECASE_SLEEP_PEND); printf("."); DoPendingExceptionTest(pTestInfo, EXCEPTIONTEST_FLAG_CALL_KERNEL_FP, FAILURECASE_API_PEND); return 0; } VOID DoPendingExceptionTest( IN PTEST_INFO TestInfo, IN ULONG ExceptionTestFlags, IN ULONG FailureCode ) { unsigned int i, count; FPXERR status; FP_THREAD_DATA fpThreadData; count = (ExceptionTestFlags & EXCEPTIONTEST_FLAG_SPIN) ? 200000 : 20000; FPxInit(&fpThreadData); for (i = 0; i < count; ++i) { status = FPxTestExceptions(i, FPxTestCallback, &fpThreadData, &ExceptionTestFlags); if (status != stOK) { fprintf(stderr, "\n\nNpx Exception Test Failed:\n"); switch (status) { case stMISSING_EXCEPTION: fprintf(stderr, "Missing exception\n"); break; case stMISSING_EXCEPTION_FOUND: fprintf(stderr, "Exception delayed unexpectedly\n"); break; case stBAD_EIP: fprintf(stderr, "Unexpected exception at %08x (expected: %08x)\n", fpThreadData.BadEip, fpThreadData.ExpectedExceptionEIP); break; case stBAD_TAG: fprintf(stderr, "Bad tag value. Expected: %e Received: %e\n", fpThreadData.Ftag, fpThreadData.FtagBad); break; case stSPURIOUS_EXCEPTION: fprintf(stderr, "Unexpected Exception at: %08x\n", fpThreadData.ExceptionEIP); break; case stEXCEPTION_IN_HANDLER: fprintf(stderr, "Exception in exception handler at: %08x\n", fpThreadData.ExceptionEIP); break; default: fprintf(stderr, "Unknown status\n"); break; } SetFailureFlag(TestInfo, FailureCode); return; } } } VOID DoFpControlCorruptionTest( IN OUT PTEST_INFO TestInfo ) { int i; unsigned short cw1, cw2; int troubledetected; troubledetected = 0; for(i = 0; i < 250; i++) { // unmask zero divide exception _asm { fnstcw [cw1] xor [cw1], 4 fldcw [cw1] } KModeTouchNpx(); _asm { fnstcw [cw2] } if (cw1 != cw2) { troubledetected = 1; break; } //fprintf(stderr, "Control pass %d.\n", i); } if (troubledetected) { fprintf(stderr, "\n\nFP control corruption detected.\n"); SetFailureFlag(TestInfo, FAILURECASE_CONTROL_CORRUPTION); } } VOID DoFpStatusCorruptionTest( IN OUT PTEST_INFO TestInfo ) { int i; unsigned short sw1, sw2; int troubledetected; troubledetected = 0; for(i = 0; i < 250; i++) { _asm { fnstsw [sw1] } KModeTouchNpx(); _asm { fnstsw [sw2] } if (sw1 != sw2) { troubledetected = 1; break; } //fprintf(stderr, "Status pass %d.\n", i); } if (troubledetected) { fprintf(stderr, "\n\nFP status corruption detected.\n"); SetFailureFlag(TestInfo, FAILURECASE_STATUS_CORRUPTION); } } VOID DoFpSt0CorruptionTest( IN OUT PTEST_INFO TestInfo ) { int i; unsigned short sw; int troubledetected; troubledetected = 0; // // This particular test is somewhat hoaky is in theory ST0 isn't callee // save. However, we know exactly what the below API does, so in this case // it's valid. // for(i = 0; i < 250; i++) { switch(i%3) { case 0: _asm { fld1 fld1 } break; case 1: _asm { fldpi fldpi } break; case 2: _asm { fldz fldz } break; } KModeTouchNpx(); _asm { fucom fstsw [sw] } if (!(sw & 0x4000)) { troubledetected = 1; } switch(i%3) { case 0: _asm fld1 break; case 1: _asm fldpi break; case 2: _asm fldz break; } _asm { fucom fstsw [sw] } if (!(sw & 0x4000)) { troubledetected = 1; } if (troubledetected) { break; } //fprintf(stderr, "STx pass %d.\n", i); } if (troubledetected) { fprintf(stderr, "\n\nFP register corruption detected.\n"); SetFailureFlag(TestInfo, FAILURECASE_STX_CORRUPTION); } } VOID DoFpPreinitTest( IN OUT PTEST_INFO TestInfo, IN ULONG PreInitTestFlags ) { unsigned short cw, sw; ULONG failureCode; failureCode = (PreInitTestFlags & PREINIT_FLAG_CLEANTHREAD) ? FAILURECASE_CLEAN_PREINIT : FAILURECASE_DIRTY_PREINIT; // // No FP should be used at this point. // KModeTouchNpx(); _asm { fnstcw [cw] fnstsw [sw] } if (cw != 0x27F) { fprintf( stderr, "\n\nPre-init corruption detected, incorrect precision in control word (cw = %x)\n", cw ); SetFailureFlag(TestInfo, failureCode); return; } if ((PreInitTestFlags&PREINIT_FLAG_CLEANTHREAD) && (sw != 0)) { fprintf( stderr, "\n\nPre-init corruption detected, status word should be zero on a clean thread (sw = %x)\n", sw ); SetFailureFlag(TestInfo, failureCode); return; } } VOID FPxTestCallback( IN PVOID Context ) { ULONG exceptionTestFlags; unsigned int j; exceptionTestFlags = *((PULONG) Context); if (exceptionTestFlags & EXCEPTIONTEST_FLAG_SLEEP) { // // Release time slice // Sleep(0); } if (exceptionTestFlags & EXCEPTIONTEST_FLAG_CALL_KERNEL_FP) { // // Use FP in K-Mode // KModeTouchNpx(); } if (exceptionTestFlags & EXCEPTIONTEST_FLAG_SPIN) { j=0x1000; while(j) j--; } }