You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
521 lines
11 KiB
521 lines
11 KiB
/*++
|
|
|
|
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 <threads>\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; i<threadCount; i++) {
|
|
|
|
hThreadArray[i] = CreateThread(
|
|
NULL,
|
|
0,
|
|
FpThread,
|
|
(LPVOID) &testInfo,
|
|
0,
|
|
&dwThreadId
|
|
);
|
|
}
|
|
|
|
WaitForMultipleObjects(threadCount, hThreadArray, TRUE, INFINITE);
|
|
|
|
DeleteCriticalSection(&testInfo.Crit);
|
|
|
|
for (i=0; i<threadCount; i++) {
|
|
|
|
CloseHandle(hThreadArray[i]);
|
|
}
|
|
|
|
handle = testInfo.FailureFlags ? stderr : stdout;
|
|
|
|
fprintf(handle, "\n\n\n");
|
|
fprintf(handle, "Test Summary:\n");
|
|
fprintf(handle, "-------------\n");
|
|
PrintResult(handle, "Dirty preinit test:", &testInfo, FAILURECASE_DIRTY_PREINIT);
|
|
PrintResult(handle, "Clean preinit test:", &testInfo, FAILURECASE_CLEAN_PREINIT);
|
|
PrintResult(handle, "Control test:", &testInfo, FAILURECASE_CONTROL_CORRUPTION);
|
|
PrintResult(handle, "Status test:", &testInfo, FAILURECASE_STATUS_CORRUPTION);
|
|
PrintResult(handle, "STx test:", &testInfo, FAILURECASE_STX_CORRUPTION);
|
|
PrintResult(handle, "Pending thrash test:", &testInfo, FAILURECASE_SPIN_PEND);
|
|
PrintResult(handle, "Pending sleep test:", &testInfo, FAILURECASE_SLEEP_PEND);
|
|
PrintResult(handle, "Pending API test:", &testInfo, FAILURECASE_API_PEND);
|
|
|
|
free(hThreadArray);
|
|
|
|
exit(testInfo.FailureFlags);
|
|
}
|
|
|
|
|
|
VOID
|
|
PrintResult(
|
|
IN FILE *Handle,
|
|
IN LPTSTR TestText,
|
|
IN PTEST_INFO TestInfo,
|
|
IN ULONG FailureFlags
|
|
)
|
|
{
|
|
fprintf(Handle, "%20s", TestText);
|
|
|
|
if (TestInfo->FailureFlags & 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--;
|
|
}
|
|
}
|
|
|