Usando ponto flutuante em um driver WDM

Última atualização

  • Julho de 2016

Os drivers WDM no modo kernel para Windows devem seguir determinadas diretrizes ao usar operações de ponto flutuante. Elas diferem entre sistemas x86 e x64. Por padrão, o Windows desativa exceções aritméticas para ambos os sistemas.

Sistemas x86

Os drivers WDM no modo kernel para sistemas x86 devem encapsular o uso de cálculos de ponto flutuante entre chamadas para KeSaveExtendedProcessorState e KeRestoreExtendedProcessorState. As operações de ponto flutuante devem ser colocadas em uma sub-rotina não embutida para garantir que os cálculos de ponto flutuante não sejam executados antes de verificar o valor retornado de KeSaveExtendedProcessorState devido à reordenação do compilador.

O compilador usa MMX/x87 também conhecido como fpu (unidade de ponto flutuante) para esses cálculos, que podem ser usados simultaneamente por um aplicativo de modo de usuário. Falha ao salvar esses registros antes de usá-los ou falha ao restaurá-los quando concluído, pode causar erros de cálculo em aplicativos.

Os drivers para sistemas x86 podem chamar KeSaveExtendedProcessorState e executar cálculos de ponto flutuante em IRQL <= DISPATCH_LEVEL. Não há suporte para operações de ponto flutuante em ISRs (rotinas de serviço de interrupção) em sistemas x86.

Sistemas x64

O compilador de 64 bits não usa os registros MMX/x87 para operações de ponto flutuante. Em vez disso, ele usa os registros SSE. O código do modo kernel x64 não tem permissão para acessar os registros MMX/x87. O compilador também cuida de salvar e restaurar corretamente o estado SSE, portanto, as chamadas para KeSaveExtendedProcessorState e KeRestoreExtendedProcessorState são desnecessárias e operações de ponto flutuante podem ser usadas em ISRs. O uso de outros recursos estendidos do processador, como o AVX, requer o salvamento e a restauração do estado estendido. Para obter mais informações, consulte Usando recursos de processador estendidos em drivers do Windows.

Observação: o Arm64 em geral é semelhante ao AMD64, pois você não precisa chamar salvar o estado do ponto flutuante primeiro. No entanto, o código que precisa ser portátil para x86 em kernels ainda pode precisar fazer isso para ser multiplataforma.

Exemplo

O exemplo a seguir mostra como um driver WDM deve encapsular seu acesso à 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;
}

No exemplo, a atribuição à variável de ponto flutuante ocorre entre chamadas para KeSaveExtendedProcessorState e KeRestoreExtendedProcessorState. Como qualquer atribuição a uma variável de ponto flutuante usa a FPU, os drivers devem garantir que KeSaveExtendedProcessorState tenha retornado sem erros antes de inicializar essa variável.

As chamadas anteriores são desnecessárias em um sistema x64 e inofensivas quando o sinalizador XSTATE_MASK_LEGACY é especificado. Portanto, não é necessário alterar o código ao compilar o driver para um sistema x64.

Em sistemas baseados em x86, a FPU é redefinida para seu estado padrão por uma chamada para FNINIT, após o retorno de KeSaveExtendedProcessorState.