diff --git a/shared/source/os_interface/windows/wddm/wddm.cpp b/shared/source/os_interface/windows/wddm/wddm.cpp index f82a27ea13..370b9b834b 100644 --- a/shared/source/os_interface/windows/wddm/wddm.cpp +++ b/shared/source/os_interface/windows/wddm/wddm.cpp @@ -1033,20 +1033,56 @@ bool Wddm::submit(uint64_t commandBuffer, size_t size, void *commandHeader, Wddm return status; } +bool Wddm::getDeviceExecutionState(D3DKMT_DEVICESTATE_TYPE stateType, void *privateData) { + D3DKMT_GETDEVICESTATE getDevState = {}; + NTSTATUS status = STATUS_SUCCESS; + + getDevState.hDevice = device; + getDevState.StateType = stateType; + + status = getGdi()->getDeviceState(&getDevState); + DEBUG_BREAK_IF(status != STATUS_SUCCESS); + if (status != STATUS_SUCCESS) { + return false; + } + + if (stateType == D3DKMT_DEVICESTATE_PAGE_FAULT) { + if (privateData != nullptr) { + *reinterpret_cast(privateData) = getDevState.PageFaultState; + } + return true; + } else if (stateType == D3DKMT_DEVICESTATE_EXECUTION) { + if (privateData != nullptr) { + *reinterpret_cast(privateData) = getDevState.ExecutionState; + } + return true; + } else { + return false; + } +} + bool Wddm::getDeviceState() { if (checkDeviceState) { - D3DKMT_GETDEVICESTATE getDevState = {}; - NTSTATUS status = STATUS_SUCCESS; - - getDevState.hDevice = device; - getDevState.StateType = D3DKMT_DEVICESTATE_EXECUTION; - - status = getGdi()->getDeviceState(&getDevState); - DEBUG_BREAK_IF(status != STATUS_SUCCESS); - PRINT_DEBUG_STRING(getDevState.ExecutionState == D3DKMT_DEVICEEXECUTION_ERROR_OUTOFMEMORY, stderr, "Device execution error, out of memory %d\n", getDevState.ExecutionState); - if (status == STATUS_SUCCESS) { - DEBUG_BREAK_IF(getDevState.ExecutionState != D3DKMT_DEVICEEXECUTION_ACTIVE); - return getDevState.ExecutionState == D3DKMT_DEVICEEXECUTION_ACTIVE; + D3DKMT_DEVICEEXECUTION_STATE executionState = D3DKMT_DEVICEEXECUTION_ACTIVE; + auto status = getDeviceExecutionState(D3DKMT_DEVICESTATE_EXECUTION, &executionState); + if (status) { + DEBUG_BREAK_IF(executionState != D3DKMT_DEVICEEXECUTION_ACTIVE); + if (executionState == D3DKMT_DEVICEEXECUTION_ERROR_OUTOFMEMORY) { + PRINT_DEBUG_STRING(true, stderr, "Device execution error, out of memory %d\n", executionState); + } else if (executionState == D3DKMT_DEVICEEXECUTION_ERROR_DMAPAGEFAULT) { + PRINT_DEBUG_STRING(true, stderr, "Device execution error, page fault\n", executionState); + D3DKMT_DEVICEPAGEFAULT_STATE pageFaultState = {}; + status = getDeviceExecutionState(D3DKMT_DEVICESTATE_PAGE_FAULT, &pageFaultState); + if (status) { + PRINT_DEBUG_STRING(true, stderr, "faulted gpuva 0x%" PRIx64 ", ", pageFaultState.FaultedVirtualAddress); + PRINT_DEBUG_STRING(true, stderr, "pipeline stage %d, bind table entry %u, flags 0x%x, error code(is device) %u, error code %u\n", + pageFaultState.FaultedPipelineStage, pageFaultState.FaultedBindTableEntry, pageFaultState.PageFaultFlags, + pageFaultState.FaultErrorCode.IsDeviceSpecificCodeReservedBit, pageFaultState.FaultErrorCode.DeviceSpecificCode); + } + } else if (executionState != D3DKMT_DEVICEEXECUTION_ACTIVE) { + PRINT_DEBUG_STRING(true, stderr, "Device execution error %d\n", executionState); + } + return executionState == D3DKMT_DEVICEEXECUTION_ACTIVE; } return false; } diff --git a/shared/source/os_interface/windows/wddm/wddm.h b/shared/source/os_interface/windows/wddm/wddm.h index 4c8ea9ea9a..d69a9d5aee 100644 --- a/shared/source/os_interface/windows/wddm/wddm.h +++ b/shared/source/os_interface/windows/wddm/wddm.h @@ -214,6 +214,9 @@ class Wddm : public DriverModel { } } + bool getDeviceExecutionState(D3DKMT_DEVICESTATE_TYPE stateType, void *privateData); + bool getDeviceState(); + protected: bool translateTopologyInfo(TopologyMapping &mapping); @@ -223,7 +226,6 @@ class Wddm : public DriverModel { bool createPagingQueue(); bool destroyPagingQueue(); bool destroyDevice(); - bool getDeviceState(); MOCKABLE_VIRTUAL void createPagingFenceLogger(); bool setLowPriorityContextParam(D3DKMT_HANDLE contextHandle); bool adjustEvictNeededParameter(bool evictNeeded) { diff --git a/shared/test/common/mock_gdi/mock_gdi.cpp b/shared/test/common/mock_gdi/mock_gdi.cpp index 2e41464c93..cd924bacd3 100644 --- a/shared/test/common/mock_gdi/mock_gdi.cpp +++ b/shared/test/common/mock_gdi/mock_gdi.cpp @@ -23,6 +23,8 @@ uint64_t gGpuAddressSpace = 0ull; uint32_t gLastPriority = 0ull; ADAPTER_BDF gAdapterBDF{}; D3DKMT_DEVICEEXECUTION_STATE gExecutionState = D3DKMT_DEVICEEXECUTION_ACTIVE; +NTSTATUS gGetDeviceStateExecutionReturnValue = STATUS_SUCCESS; +NTSTATUS gGetDeviceStatePageFaultReturnValue = STATUS_SUCCESS; NTSTATUS __stdcall mockD3DKMTEscape(IN CONST D3DKMT_ESCAPE *pData) { static int perfTicks = 0; @@ -646,7 +648,19 @@ NTSTATUS __stdcall mockD3DKMTEvict(IN OUT D3DKMT_EVICT *) { } NTSTATUS __stdcall mockD3DKMTGetDeviceState(IN OUT D3DKMT_GETDEVICESTATE *getDevState) { - getDevState->ExecutionState = gExecutionState; + if (getDevState->StateType == D3DKMT_DEVICESTATE_EXECUTION) { + getDevState->ExecutionState = gExecutionState; + return gGetDeviceStateExecutionReturnValue; + } else if (getDevState->StateType == D3DKMT_DEVICESTATE_PAGE_FAULT) { + getDevState->PageFaultState = {}; + getDevState->PageFaultState.FaultedPipelineStage = DXGK_RENDER_PIPELINE_STAGE_UNKNOWN; + getDevState->PageFaultState.FaultedBindTableEntry = 2; + getDevState->PageFaultState.PageFaultFlags = DXGK_PAGE_FAULT_WRITE; + getDevState->PageFaultState.FaultErrorCode.IsDeviceSpecificCodeReservedBit = 1; + getDevState->PageFaultState.FaultErrorCode.DeviceSpecificCode = 10; + getDevState->PageFaultState.FaultedVirtualAddress = 0xABC000; + return gGetDeviceStatePageFaultReturnValue; + } return STATUS_SUCCESS; } @@ -756,3 +770,11 @@ D3DKMT_SETCONTEXTSCHEDULINGPRIORITY *getSetContextSchedulingPriorityDataCall() { void setMockDeviceExecutionState(D3DKMT_DEVICEEXECUTION_STATE newState) { gExecutionState = newState; } + +void setMockGetDeviceStateReturnValue(NTSTATUS newReturnValue, bool execution) { + if (execution) { + gGetDeviceStateExecutionReturnValue = newReturnValue; + } else { + gGetDeviceStatePageFaultReturnValue = newReturnValue; + } +} diff --git a/shared/test/common/mock_gdi/mock_gdi.h b/shared/test/common/mock_gdi/mock_gdi.h index 81dd71ac8b..cf6b4b76c2 100644 --- a/shared/test/common/mock_gdi/mock_gdi.h +++ b/shared/test/common/mock_gdi/mock_gdi.h @@ -104,5 +104,5 @@ bool *getRegisterTrimNotificationFailCall(); uint32_t getLastPriority(); void setAdapterBDF(ADAPTER_BDF &adapterBDF); void setMockDeviceExecutionState(D3DKMT_DEVICEEXECUTION_STATE newState); - +void setMockGetDeviceStateReturnValue(NTSTATUS newReturnValue, bool execution); void initGfxPartition(); diff --git a/shared/test/common/mock_gdi/mock_os_library.cpp b/shared/test/common/mock_gdi/mock_os_library.cpp index a899df1958..793f9ebc02 100644 --- a/shared/test/common/mock_gdi/mock_os_library.cpp +++ b/shared/test/common/mock_gdi/mock_os_library.cpp @@ -133,6 +133,9 @@ void *MockOsLibrary::getProcAddress(const std::string &procName) { if (procName == "setMockDeviceExecutionState") { return reinterpret_cast(setMockDeviceExecutionState); } + if (procName == "setMockGetDeviceStateReturnValue") { + return reinterpret_cast(setMockGetDeviceStateReturnValue); + } if (procName == "getMockAllocation") { return reinterpret_cast(getMockAllocation); } diff --git a/shared/test/common/os_interface/windows/gdi_dll_fixture.h b/shared/test/common/os_interface/windows/gdi_dll_fixture.h index 653408a215..9f485b278b 100644 --- a/shared/test/common/os_interface/windows/gdi_dll_fixture.h +++ b/shared/test/common/os_interface/windows/gdi_dll_fixture.h @@ -58,6 +58,7 @@ struct GdiDllFixture { setAdapterBDFFcn = reinterpret_cast(mockGdiDll->getProcAddress("setAdapterBDF")); setMockDeviceExecutionStateFcn = reinterpret_cast(mockGdiDll->getProcAddress("setMockDeviceExecutionState")); + setMockGetDeviceStateReturnValueFcn = reinterpret_cast(mockGdiDll->getProcAddress("setMockGetDeviceStateReturnValue")); setMockLastDestroyedResHandleFcn((D3DKMT_HANDLE)0); *getDestroySynchronizationObjectDataFcn() = {}; *getCreateSynchronizationObject2FailCallFcn() = false; @@ -104,4 +105,5 @@ struct GdiDllFixture { decltype(&getLastPriority) getLastPriorityFcn = nullptr; decltype(&setAdapterBDF) setAdapterBDFFcn = nullptr; decltype(&setMockDeviceExecutionState) setMockDeviceExecutionStateFcn = nullptr; + decltype(&setMockGetDeviceStateReturnValue) setMockGetDeviceStateReturnValueFcn = nullptr; }; diff --git a/shared/test/unit_test/os_interface/windows/wddm_tests.cpp b/shared/test/unit_test/os_interface/windows/wddm_tests.cpp index 80703d0ba3..d24ddb5be0 100644 --- a/shared/test/unit_test/os_interface/windows/wddm_tests.cpp +++ b/shared/test/unit_test/os_interface/windows/wddm_tests.cpp @@ -490,12 +490,37 @@ TEST_F(WddmTests, givenCheckDeviceStateSetToTrueWhenCallGetDeviceStateAndForceEx wddm->checkDeviceState = true; auto executionState = D3DKMT_DEVICEEXECUTION_ERROR_OUTOFMEMORY; setMockDeviceExecutionStateFcn(executionState); + ::testing::internal::CaptureStderr(); EXPECT_FALSE(wddm->getDeviceState()); std::string output = testing::internal::GetCapturedStderr(); EXPECT_EQ(std::string("Device execution error, out of memory " + std::to_string(executionState) + "\n"), output); setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + + ::testing::internal::CaptureStderr(); + EXPECT_TRUE(wddm->getDeviceState()); + output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string(""), output); +} + +TEST_F(WddmTests, givenCheckDeviceStateSetToTrueWhenCallGetDeviceStateReturnsFailThenNoMessageIsVisible) { + DebugManagerStateRestore restorer{}; + DebugManager.flags.EnableDebugBreak.set(false); + + wddm->checkDeviceState = true; + auto executionState = D3DKMT_DEVICEEXECUTION_ERROR_OUTOFMEMORY; + setMockDeviceExecutionStateFcn(executionState); + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS + 1, true); + + ::testing::internal::CaptureStderr(); + EXPECT_FALSE(wddm->getDeviceState()); + std::string output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string(""), output); + + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS, true); + ::testing::internal::CaptureStderr(); EXPECT_TRUE(wddm->getDeviceState()); output = testing::internal::GetCapturedStderr(); @@ -509,18 +534,114 @@ TEST_F(WddmTests, givenCheckDeviceStateSetToFalseWhenCallGetDeviceStateAndForceE wddm->checkDeviceState = false; auto executionState = D3DKMT_DEVICEEXECUTION_ERROR_OUTOFMEMORY; setMockDeviceExecutionStateFcn(executionState); + ::testing::internal::CaptureStderr(); EXPECT_TRUE(wddm->getDeviceState()); std::string output = testing::internal::GetCapturedStderr(); EXPECT_EQ(std::string(""), output); setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + ::testing::internal::CaptureStderr(); EXPECT_TRUE(wddm->getDeviceState()); output = testing::internal::GetCapturedStderr(); EXPECT_EQ(std::string(""), output); } +TEST_F(WddmTests, givenCheckDeviceStateSetToTrueWhenCallGetDeviceStateReturnsPageFaultThenProperMessageIsVisible) { + DebugManagerStateRestore restorer{}; + DebugManager.flags.EnableDebugBreak.set(false); + + wddm->checkDeviceState = true; + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ERROR_DMAPAGEFAULT); + + ::testing::internal::CaptureStderr(); + EXPECT_FALSE(wddm->getDeviceState()); + std::string output = testing::internal::GetCapturedStderr(); + std::string expected = "Device execution error, page fault\nfaulted gpuva 0xabc000, pipeline stage 0, bind table entry 2, flags 0x1, error code(is device) 1, error code 10\n"; + EXPECT_EQ(expected, output); + + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + + ::testing::internal::CaptureStderr(); + EXPECT_TRUE(wddm->getDeviceState()); + output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string(""), output); +} + +TEST_F(WddmTests, givenCheckDeviceStateSetToTrueWhenCallGetDeviceStateReturnsFailureThenBasicMessageDisplayed) { + DebugManagerStateRestore restorer{}; + DebugManager.flags.EnableDebugBreak.set(false); + + wddm->checkDeviceState = true; + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ERROR_DMAPAGEFAULT); + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS + 1, false); + + ::testing::internal::CaptureStderr(); + EXPECT_FALSE(wddm->getDeviceState()); + std::string output = testing::internal::GetCapturedStderr(); + std::string expected = "Device execution error, page fault\n"; + EXPECT_EQ(expected, output); + + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS, false); + + ::testing::internal::CaptureStderr(); + EXPECT_TRUE(wddm->getDeviceState()); + output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string(""), output); +} + +TEST_F(WddmTests, givenCheckDeviceStateSetToTrueWhenCallGetDeviceStateReturnsOtherNonActiveStateThenGenericMessageDisplayed) { + DebugManagerStateRestore restorer{}; + DebugManager.flags.EnableDebugBreak.set(false); + + wddm->checkDeviceState = true; + auto executionState = D3DKMT_DEVICEEXECUTION_RESET; + setMockDeviceExecutionStateFcn(executionState); + + ::testing::internal::CaptureStderr(); + EXPECT_FALSE(wddm->getDeviceState()); + std::string output = testing::internal::GetCapturedStderr(); + std::string expected = std::string("Device execution error " + std::to_string(executionState) + "\n"); + EXPECT_EQ(expected, output); + + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + + ::testing::internal::CaptureStderr(); + EXPECT_TRUE(wddm->getDeviceState()); + output = testing::internal::GetCapturedStderr(); + EXPECT_EQ(std::string(""), output); +} + +TEST_F(WddmTests, givenGetDeviceExecutionStatusWhenGdiCallFailsThenReturnFalse) { + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS + 1, true); + setMockDeviceExecutionStateFcn(D3DKMT_DEVICEEXECUTION_ACTIVE); + + auto status = wddm->getDeviceExecutionState(D3DKMT_DEVICESTATE_EXECUTION, nullptr); + EXPECT_FALSE(status); + + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS, true); + + status = wddm->getDeviceExecutionState(D3DKMT_DEVICESTATE_EXECUTION, nullptr); + EXPECT_TRUE(status); +} + +TEST_F(WddmTests, givenGetDeviceExecutionStatusWhenUnsupportedStatusProvidedThenReturnsFalse) { + auto status = wddm->getDeviceExecutionState(D3DKMT_DEVICESTATE_PRESENT, nullptr); + EXPECT_FALSE(status); +} + +TEST_F(WddmTests, givenGetDeviceExecutionStatusWhenGettingPageFaultStatusReturnsFailThenReturnFalse) { + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS + 1, false); + auto status = wddm->getDeviceExecutionState(D3DKMT_DEVICESTATE_PAGE_FAULT, nullptr); + EXPECT_FALSE(status); + + setMockGetDeviceStateReturnValueFcn(STATUS_SUCCESS, false); + status = wddm->getDeviceExecutionState(D3DKMT_DEVICESTATE_PAGE_FAULT, nullptr); + EXPECT_TRUE(status); +} + TEST(WddmConstructorTest, givenEnableDeviceStateVerificationSetTrueWhenCreateWddmThenCheckDeviceStateIsTrue) { DebugManagerStateRestore restorer{}; DebugManager.flags.EnableDeviceStateVerification.set(1);