在 WDM 驅動程式中使用浮點

上次更新日期

  • 2016 年 7 月

Windows 的核心模式 WDM 驅動程式在使用浮點運算時,必須遵循特定指導方針。 這些在 x86 和 x64 系統之間有所不同。 根據預設,Windows 會關閉這兩個系統的算術例外狀況。

x86 系統

x86 系統的核心模式 WDM 驅動程式必須包裝呼叫 KeSaveExtendedProcessorStateKeRestoreExtendedProcessorState之間的浮點計算。 浮點運算必須放在非內嵌副程式中,以確保在檢查 KeSaveExtendedProcessorState 的傳回值之前,不會執行浮點計算,因為編譯器重新排序。

編譯器會使用 MMX/x87 也稱為浮點單位 (FPU) 註冊這類計算,使用者模式應用程式可以同時使用。 在使用這些暫存器之前無法儲存這些暫存器,或在完成時無法還原暫存器,可能會導致應用程式中的計算錯誤。

x86 系統的驅動程式可以呼叫 KeSaveExtendedProcessorState ,並在 IRQL < = DISPATCH_LEVEL執行浮點計算。 x86 系統上的插斷服務常式 (ISR) 不支援浮點作業。

x64 系統

64 位編譯器不會使用 MMX/x87 暫存器進行浮點運算。 相反地,它會使用 SSE 暫存器。 不允許 x64 核心模式程式碼存取 MMX/x87 暫存器。 編譯器也會負責正確儲存和還原 SSE 狀態,因此, 對 KeSaveExtendedProcessorStateKeRestoreExtendedProcessorState 的呼叫是不必要的,而且可以在 ISR 中使用浮點運算。 使用其他擴充處理器功能,例如 AVX,需要儲存和還原擴充狀態。 如需詳細資訊,請參閱 在 Windows 驅動程式中使用擴充處理器功能

注意:一般而言,Arm64 類似于 AMD64,您不需要先呼叫儲存浮點狀態。 不過,在核心上必須可移植到 x86 的程式碼,仍需要執行該動作才能跨平臺。

範例

下列範例示範 WDM 驅動程式如何包裝其 FPU 存取權:

__declspec(noinline)
VOID
DoFloatingPointCalculation(
    VOID
    )
{
    double Duration;
    LARGE_INTEGER Frequency;

    Duration = 1000000.0;
    DbgPrint("%I64x\n", *(LONGLONG*)&Duration);
    KeQueryPerformanceCounter(&Frequency);
    Duration /= (double)Frequency.QuadPart;
    DbgPrint("%I64x\n", *(LONGLONG*)&Duration);
}

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{

    XSTATE_SAVE SaveState;
    NTSTATUS Status;

    Status = KeSaveExtendedProcessorState(XSTATE_MASK_LEGACY, &SaveState);
    if (!NT_SUCCESS(Status)) {
        goto exit;
    }

    __try {
        DoFloatingPointCalculation();
    }
    __finally {
        KeRestoreExtendedProcessorState(&SaveState);
    }

exit:
    return Status;
}

在此範例中,呼叫 KeSaveExtendedProcessorStateKeRestoreExtendedProcessorState之間會發生浮點變數的指派。 由於對浮點變數的任何指派都會使用 FPU,因此驅動程式必須確定 KeSaveExtendedProcessorState 在初始化這類變數之前已傳回,而不會發生錯誤。

在 x64 系統上不需要上述呼叫,而且在指定XSTATE_MASK_LEGACY旗標時不會造成傷害。 因此,編譯 x64 系統的驅動程式時,不需要變更程式碼。

在 x86 型系統上,FPU 會在從 KeSaveExtendedProcessorState傳回時呼叫 FNINIT,重設為其預設狀態。