Partilhar via


Desenvolver módulos de extensibilidade de transporte KDNET

Este tópico descreve como o transporte KDNET pode ser estendido para ser executado em qualquer hardware por meio do uso de um módulo dll de extensibilidade de driver de hardware separado. Os módulos de extensibilidade de transporte KDNET são desenvolvidos por fornecedores de placas de rede para adicionar suporte de depuração do kernel a placas de rede específicas.

Visão geral do KDNET

KDNET é um transporte de depuração do kernel que permite a depuração do kernel de janelas por meio de uma rede. Inicialmente, ele foi usado para dar suporte à depuração de kernel com NICs Ethernet. Ele foi projetado para que a camada de suporte de hardware seja criada em um módulo separado do processamento de pacotes de rede e da camada de interface do kernel. Essa camada de suporte de driver de hardware é chamada de módulo de extensibilidade KDNET.

KDNET é o único transporte que pode ser estendido para ser executado em qualquer hardware por meio do uso de um módulo dll de extensibilidade de driver de hardware separado. O objetivo é dar suporte a toda a depuração do Windows por meio do KDNET e dos módulos de extensibilidade KDNET. Todas as outras DLLs de transporte do kernel (kdcom.dll, kd1394.dll, kdusb.dll, etc.) serão consequentemente preteridas e removidas do Windows.

Existem dois tipos de interfaces que o KDNET usa para se comunicar com os módulos de extensibilidade KDNET. Uma é uma interface baseada em pacotes que é usada para NICs, USB e hardware sem fio, e a outra é uma interface baseada em bytes que é usada para dar suporte ao KDNET via hardware serial.

Os módulos de extensibilidade KDNET devem seguir requisitos muito rigorosos para operar de forma correta. Como eles são usados para depuração do kernel, eles serão chamados e executados quando o sistema estiver adiando a execução adicional do código. Geralmente, todos os processadores no sistema são bloqueados com a rotação de um IPI, exceto o processador que está se comunicando com o aplicativo do depurador em execução no computador host pelo transporte de depuração do kernel. Esse processador geralmente está sendo executado com interrupções completamente desabilitadas e está basicamente girando no hardware de transporte de depuração que está esperando que os comandos venham do depurador.

Importação e exportação

Os módulos de extensibilidade KDNET têm exatamente uma exportação explícita – KdInitializeLibrary. Eles também não têm importações explícitas. Os módulos de extensibilidade KDNET passam um ponteiro para uma estrutura com uma lista das rotinas que eles têm permissão para chamar pelo KDNET quando ele chama KdInitializeLibrary. Nenhuma outra rotina pode ser chamada. Ponto final. Os módulos de extensibilidade KDNET que têm importações foram projetados incorretamente e não terão suporte.

Se você despejar as importações e exportações de módulos de extensibilidade KDNET usando link /despejo /exportações e link /despejo /importações, verá que eles têm apenas uma exportação (KdInitializeLibrary) e nenhuma importação. Os módulos de extensibilidade KDNET relatam suas exportações adicionais para o KDNET preenchendo ponteiros de função em uma estrutura de funções de exportação para a qual o KDNET passa um ponteiro quando KdInitializeLibrary é chamado. O KDNET usa os ponteiros de função nessa estrutura para chamar o módulo de extensibilidade e efetuar transferências de dados usando o hardware compatível com o módulo. O KDNET determina se o módulo é um módulo baseado em pacotes ou baseado em bytes, observando quais funções específicas o módulo preenche na tabela de funções de exportação na estrutura. Algumas dessas funções são para dar suporte ao hardware baseado em pacotes, e outras são para hardware baseado em série. Algumas das funções na tabela são usadas por hardware serial e baseado em pacotes (KdInitializeController, KdShutdownController, KdGetHardwareContextSize).

Design de código

Os módulos de extensibilidade KDNET devem ser escritos como código de thread único. Eles não devem executar nenhuma sincronização. Todos os transportes de depuração do kernel dependem do kernel do Windows para fazer a sincronização adequada quando o depurador é inserido. O kernel tem um bloqueio de depurador que ele leva quando entra no depurador do kernel, e também bloqueia os outros processadores no sistema em um IPI quando o depurador é inserido. Esses processadores serão liberados somente quando o depurador do kernel em execução no host solicitar ao computador de destino para permitir que a execução continue. Como o kernel faz essa sincronização, os módulos de extensibilidade KDNET não devem absolutamente usar spinlocks, mutexes, gates ou qualquer outro mecanismo de sincronização do Windows em seu código. Eles devem ser escritos para programar diretamente seu respectivo hardware para enviar e receber pacotes e/ou bytes.

O código do módulo de extensibilidade KDNET deve ser criado da forma mais simples possível. Isso ajudará a garantir que ele esteja o mais livre de bugs possível, já que a depuração do código do módulo de extensibilidade KDNET ativo em um computador não é possível atualmente sem o uso de um depurador de hardware. Você não pode usar o depurador do kernel para depurar o código de transporte de depuração do kernel. Tentar fazer isso fará com que o computador seja reinicializado devido a uma pilha de kernel explodida (que geralmente termina com uma falha dupla e reinicialização), ou deadlock, ou fará com que o transporte seja reinserido, o que na maioria dos casos fará com que o transporte não funcione corretamente.

Convenções de nomenclatura de módulos de extensibilidade KDNET

Seu módulo de transporte de depuração do kernel deve seguir uma das duas convenções de nomenclatura para módulos de extensibilidade KDNET. Se o seu módulo se destina a ter suporte de hardware baseado em PCI, deverá ser nomeado kd_YY_XXXX.dll, em que XXXX é a ID do fornecedor PCI do seu hardware em hexadecimal, e YY é a classe PCI para o seu hardware. Existem vários módulos de extensibilidade KDNET que são fornecidos na caixa em janelas que têm suporte para hardware baseado em PCI. Por exemplo, o kd_02_8086.dll da Intel, o kd_02_14e4.dll da Broadcom e o kd_02_10ec.dll da Realtek. Você pode procurar IDs de fornecedor PCI registradas em https://www.pcisig.com/membership/member-companies. Todos os módulos de extensibilidade KDNET baseados em PCI usam a VID do fornecedor do hardware que têm suporte em hexadecimal, como os últimos 4 caracteres no nome de seu módulo. O código de classe para a maioria dos módulos nativos é 02, porque eles são dispositivos de classe de rede e, portanto, têm uma classe PCI de 0x02 em seu espaço de configuração de PCI. Winload.exe cria o nome dos módulos de extensibilidade KDNET baseados em PCI lendo a classe de dispositivo PCI e a VID PCI do dispositivo de depuração selecionado a partir de seu espaço de configuração PCI e tenta carregar um módulo com esses identificadores no nome. Se o seu dispositivo tiver um código de classe PCI que não seja a classe de rede 0x02, você deverá usar o código de classe PCI correto em hexadecimal para o seu dispositivo, no nome do seu módulo de extensibilidade KDNET. Caso contrário, seu módulo não será carregado corretamente pelo winload. O _02_ em cada um desses nomes é o código de classe PCI para dispositivos de classe de rede em hexadecimal. Esse código também é encontrado e lido no espaço de configuração de PCI do dispositivo de depuração.

Se você tiver um dispositivo que tenha uma entrada de tabela DBG2 e não seja um dispositivo baseado em PCI, a convenção de nomenclatura para o módulo será diferente. A convenção de nomenclatura para dispositivos de depuração de tabela DBG2 é kd_XXXX_YYYY.dll, em que XXXX é a tabela DBG2 hexadecimal PortType e YYYY é a tabela DBG2 hexadecimal PortSubtype da entrada de tabela DBG2. Kd_8003_5143.dll é uma DLL nativa para oferecer suporte a um PortType de rede (0x8003) com um subtipo de 0x5143. Neste caso, 5143 é a Qualcomm PCI vid, uma vez que deve ter suporte para KDNET em controladores USB Qualcomm, e para entradas de tabela Net DBG2, o PortSubtype é definido para ser a PCI VID para o fornecedor do hardware. Observe que você pode oferecer suporte a dispositivos de tabela seriais, USB e outros dispositivos de tabela DBG2 que usam essa convenção de nomenclatura. A seguir estão os valores de PortType com suporte atualmente em hexadecimal: 8000 para dispositivos seriais, 8001 para dispositivos 1394, 8002 para dispositivos USB, 8003 para dispositivos NET. Os subtipos para dispositivos seriais e USB devem ser reservados com a Microsoft. A Microsoft mantém uma lista dos subtipos seriais e USB alocados. Envie um email para kdnet@microsoft.com para reservar um subtipo serial ou USB, se os tipos com suporte existentes não funcionarem com seu hardware.

Importações de extensibilidade KDNET

A seguir está a lista de rotinas que você pode chamar de um módulo de extensibilidade KDNET. Todas essas rotinas são passadas para a rotina KdInitializeLibrary, e o cabeçalho kdnetextensibility.h remapeará chamadas normais para essas rotinas para passar pela tabela de importação. Seu código deve chamá-las por meio da tabela de importação, para que seu módulo não tenha importações. Você não pode chamar nenhuma outra rotina que seja exportada pelo kernel, pelo HAL ou por qualquer outro módulo do kernel. Você só pode chamar essas rotinas. Este conjunto de rotinas provou ser suficiente para desenvolver todos os módulos de extensibilidade KDNET nativos e deve ser suficiente para cenários normais. Se você precisar de rotinas adicionais que são exportadas pelo kernel, mas não estão nesta lista, envie um email para kdnet@microsoft.com explicando seu cenário, e quais rotinas adicionais você precisa e por quê. Essa lista só será adicionada aos principais ciclos de lançamento do Windows, se houver. A maioria dessas rotinas corresponde diretamente às APIs do kernel do Windows que têm suporte pelo kernel ou pelo HAL. Uma ou duas são rotinas personalizadas somente do KDNET.

É fundamental que você inclua kdnetextensibility.h corretamente em seus cabeçalhos para que possa ocorrer o remapeamento correto de rotinas por meio da tabela de importação. Se isso não for feito, seu módulo terá importações e não terá suporte.

Rotinas de memória de leitura e gravação

As rotinas a seguir devem ser usadas para leitura e gravação na memória mapeada do dispositivo. Estas têm a mesma convenção de chamada e são mapeadas para suas rotinas de kernel correspondentes: READ_REGISTER_UCHAR, READ_REGISTER_USHORT, READ_REGISTER_ULONG, WRITE_REGISTER_UCHAR, WRITE_REGISTER_USHORT, WRITE_REGISTER_ULONG e em plataformas de 64 bits apenas READ_REGISTER_ULONG64 e WRITE_REGISTER_ULONG64. Todo o acesso à memória do dispositivo deve ser feito por meio dessas rotinas, pois elas garantirão que as leituras e gravações não sejam reordenadas pelo processador. Observe que o msdn.microsoft.com documenta as rotinas do Windows CE Compact 2013 que correspondem na convenção de chamada a essas rotinas. Infelizmente, parece que as rotinas NT não estão documentadas, mas a convenção de chamada é a mesma.

Ler rotinas de porta de E/S

As rotinas a seguir devem ser usadas para leitura e gravação nas portas de E/S do dispositivo. Estas têm a mesma convenção de chamada e são mapeadas para suas rotinas de kernel correspondentes: READ_PORT_UCHAR, READ_PORT_USHORT, READ_PORT_ULONG, WRITE_PORT_UCHAR, WRITE_PORT_USHORT e WRITE_PORT_ULONG. Todo o acesso à porta de E/S do dispositivo deve ser feito por meio dessas rotinas. Observe que o msdn.microsoft.com documenta as rotinas do Windows CE Compact 2013 que correspondem na convenção de chamada a essas rotinas.

Rotinas adicionais

As rotinas adicionais a seguir podem ser chamadas e devem ser chamadas normalmente com os parâmetros especificados. Observe que fazer isso, ao incluir corretamente o cabeçalho kdnetextensibility.h, irá remapear as chamadas de função por meio da tabela de importação de extensibilidade KDNET, resultando em nenhuma importação explícita em seu módulo, como é requerido para os módulos de extensibilidade KDNET.

PHYSICAL_ADDRESS

KdGetPhysicalAddress (

    __in PVOID Va

    );
 

VOID

KeStallExecutionProcessor (

    __in ULONG Microseconds

    );


ULONG

KdGetPciDataByOffset (

    __in ULONG BusNumber,

    __in ULONG SlotNumber,

    __out_bcount(Length) PVOID Buffer,

    __in ULONG Offset,

    __in ULONG Length

    );
 

ULONG

KdSetPciDataByOffset (

    __in ULONG BusNumber,

    __in ULONG SlotNumber,

    __in_bcount(Length) PVOID Buffer,

    __in ULONG Offset,

    __in ULONG Length

    );

 
VOID

KdSetDebuggerNotPresent (

    __in BOOLEAN NotPresent

    );
 

VOID

PoSetHiberRange (

    _In_opt_ PVOID MemoryMap,

    _In_ ULONG     Flags,

    _In_ PVOID     Address,

    _In_ ULONG_PTR Length,

    _In_ ULONG     Tag

    );

 

VOID

KeBugCheckEx (

    __in ULONG BugCheckCode,

    __in ULONG_PTR BugCheckParameter1,

    __in ULONG_PTR BugCheckParameter2,

    __in ULONG_PTR BugCheckParameter3,

    __in ULONG_PTR BugCheckParameter4

    );


PVOID

KdMapPhysicalMemory64 (

    _In_ PHYSICAL_ADDRESS PhysicalAddress,

    _In_ ULONG NumberPages,

    _In_ BOOLEAN FlushCurrentTLB

    );
 

VOID

KdUnmapVirtualAddress (

    _In_ PVOID VirtualAddress,

    _In_ ULONG NumberPages,

    _In_ BOOLEAN FlushCurrentTLB

    );
 

ULONG64

KdReadCycleCounter (

    __out_opt PULONG64 Frequency

    );

A função PoSetHiberRange só deve ser chamada a partir da rotina KdSetHibernateRange. Além disso, a maioria dos módulos de extensibilidade KDNET não precisa chamar KeBugCheckEx, KdMapPhysicalMemory64 e KdUnmapVirtualAddress. Por outro lado, basicamente, todos os módulos de extensibilidade KDNET precisarão chamar KdGetPhysicalAddress para obter endereços de memória física necessários para programar os mecanismos DMA do dispositivo, e muitos precisarão chamar KeStallExecutionProcessor, KdGetPciDataByOffset e KdSetPciDataByOffset. As duas últimas rotinas são para acessar o espaço de configuração PCI do dispositivo.

Exportações de extensibilidade KDNET

A seguir está uma breve descrição de cada uma das rotinas de extensibilidade KDNET. Você deve implementar todas as rotinas necessárias para um módulo de extensibilidade KDNET baseado em pacotes ou um módulo de extensibilidade KDNET baseado em série. A seguir estão as exportações do módulo de extensibilidade KDNET do pacote.

KdInitializeLibrary

/*++

Routine Description:

    This routine validates that the ImportTable is a supported version.  Makes
    a copy of the ImportTable in its own global memory, and writes pointers to
    functions that it exports into the Exports pointer also located in that
    table.

    This routine also writes the size in bytes of the Memory it needs into
    the Length field of the Memory structure contained in the debug device
    descriptor passed to this routine.

    When kernel debugging is enabled, this routine will be called twice during
    boot.  The first time by winload to determine how much memory to allocate
    for KDNET and its extensibility module, and the second time by KDNET when
    the kernel first initializes the kernel debugging subsystem.

Arguments:

    ImportTable - Supplies a pointer to the KDNET_EXTENSIBILITY_IMPORT
        structure.

    LoaderOptions - Supplies a pointer to the LoaderOptions passed to the
        kernel.  This allows settings to be passed to the KDNET extensibility
        module using the loadoptions BCD setting.

    Device - Supplies a pointer to the debug device descriptor.

Return Value:

    STATUS_INVALID_PARAMETER if the version of the import or export table is
        incorrect.

    STATUS_SUCCESS if initialization succeeds.

--*/
NTSTATUS
KdInitializeLibrary (
    __in PKDNET_EXTENSIBILITY_IMPORTS ImportTable,
    __in_opt PCHAR LoaderOptions,
    __inout PDEBUG_DEVICE_DESCRIPTOR Device
    )
{
    NTSTATUS Status;
    PKDNET_EXTENSIBILITY_EXPORTS Exports;

    __security_init_cookie();
    Status = STATUS_SUCCESS;
    KdNetExtensibilityImports = ImportTable;
    if ((KdNetExtensibilityImports == NULL) ||
        (KdNetExtensibilityImports->FunctionCount != KDNET_EXT_IMPORTS)) {

        Status = STATUS_INVALID_PARAMETER;
        goto KdInitializeLibraryEnd;
    }

    Exports = KdNetExtensibilityImports->Exports;
    if ((Exports == NULL) || (Exports->FunctionCount != KDNET_EXT_EXPORTS)) {
        Status = STATUS_INVALID_PARAMETER;
        goto KdInitializeLibraryEnd;
    }

    //
    // Return the function pointers this KDNET extensibility module exports.
    //

    Exports->KdInitializeController = KdInitializeController;
    Exports->KdShutdownController = KdShutdownController;
    Exports->KdSetHibernateRange = KdSetHibernateRange;
    Exports->KdGetRxPacket = KdGetRxPacket;
    Exports->KdReleaseRxPacket = KdReleaseRxPacket;
    Exports->KdGetTxPacket = KdGetTxPacket;
    Exports->KdSendTxPacket = KdSendTxPacket;
    Exports->KdGetPacketAddress = KdGetPacketAddress;
    Exports->KdGetPacketLength = KdGetPacketLength;
    Exports->KdGetHardwareContextSize = KdGetHardwareContextSize;

    //
    // Return the hardware context size required to support this device.
    //

    Status = ContosoInitializeLibrary(LoaderOptions, Device);

KdInitializeLibraryEnd:
    return Status;
}

Esta rotina é chamada para passar as rotinas de importação e exportação entre o KDNET e este módulo de extensibilidade KDNET. Essa rotina deve validar se a versão das tabelas de importação e exportação é esperada e compatível. Se não for, ela falhará. Deve fazer uma cópia da tabela de importação em sua própria memória global. Deve escrever as rotinas que exporta na estrutura apontada pelo campo Exportações da tabela de importação. Também deve definir o campo Comprimento da estrutura Memória que faz parte do ponteiro do descritor de dispositivo de depuração passado para essa rotina, com o número de bytes de memória necessário para dar suporte ao dispositivo de hardware.

Validar contagem de importação e exportação

O código deve verificar se Imports FunctionCount corresponde ao que está disponível no sistema operacional, por exemplo, e (KdNetExtensibilityImports->FunctionCount != KDNET_EXT_IMPORTS) retornar STATUS_INVALID_PARAMETER se a contagem não corresponder.

A verificação das contagens garante a compatibilidade entre a versão KDNET do sistema operacional Windows em execução (por exemplo, gerenciador de inicialização/carregador do sistema operacional/hipervisor/kernel seguro/sistema operacional NT, todos vinculados à biblioteca KDNET) e a versão do WDK usada para criar o módulo de extensibilidade KDNET atual. O WDK e a versão do sistema operacional devem ser sincronizados; caso contrário, a verificação acima falhará se o valor do contador de importação/exportação for alterado. Por exemplo, se a interface de extensibilidade KDNET adicionou uma nova função de recurso, a contagem não corresponderá mais. Para garantir que eles correspondam, sempre use a versão do WDK que corresponde ao sistema operacional que hospeda a versão do KDNET.

Personalizar a memória necessária

O dispositivo será preenchido com o hardware selecionado para o depurador. Essa rotina deve personalizar a quantidade de memória requerida com base no dispositivo, se necessário. Por exemplo, os módulos de extensibilidade compatíveis com hardware de 1Gig e 10Gig podem aumentar o tamanho da memória solicitada para dispositivos de 10Gig. Eles podem determinar qual dispositivo está sendo usado examinando o campo DeviceID do descritor de dispositivo de depuração.

KdInitializeLibrary é a única exportação

Essa rotina será chamada tanto pelo winload quanto pelo KDNET durante a chamada de KdInitSystem. Essa é a ÚNICA rotina que é exportada pelos módulos de extensibilidade KDNET. É a única rotina colocada em um arquivo .def. Os módulos de extensibilidade KDNET têm exatamente uma exportação explícita – essa rotina – e nenhuma importação.

KdSetHibernateRange

VOID

KdSetHibernateRange (

    VOID

    )

/*++
Routine Description:

    This routine is called to mark the code in the KDNET extensiblity module
    so that it can be properly handled during hibernate and resume from
    hibernate.

Arguments:
    None.

Return Value:
    None.
--*/

Essa rotina é chamada pelo sistema antes de hibernar, para que possa registrar corretamente o código usado pelo módulo de extensibilidade KDNET com o sistema. Isso permite que o sistema gerencie corretamente essa memória durante a hibernação e retomada da hibernação. (A memória é salva tardiamente e carregada antecipadamente, já que será chamada muito precocemente durante a retomada.)

KdInitializeController

NTSTATUS

KdInitializeController(

    __in PVOID Adapter

    )

/*++
Routine Description:

    This function initializes the Network controller.  The controller is setup
    to send and recieve packets at the fastest rate supported by the hardware
    link.  Packet send and receive will be functional on successful exit of
    this routine.  The controller will be initialized with Interrupts masked
    since all debug devices must operate without interrupt support.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

Return Value:
    STATUS_SUCCESS on successful initialization.  Appropriate failure code if
    initialization fails.
--*/

Essa rotina é chamada para inicializar o hardware. Ela é chamada quando o sistema é inicializado e sempre que o sistema é ativado de um estado de baixa energia para o qual ele chamou KdShutdownController. Essa rotina DEVE garantir que o hardware tenha concluído totalmente a inicialização e que esteja pronto para enviar pacotes ANTES de retornar. Essa rotina deve aguardar a exibição de PHY e o estabelecimento do link. Observe que, se não houver nenhum cabo conectado, essa rotina não deverá parar indefinidamente. Essa rotina define a velocidade do link e o duplex na estrutura de dados compartilhados do KDNET que é compartilhada entre o KDNET e este módulo de extensibilidade. Ela também grava o endereço MAC usado pelo hardware, no local apontado por TargetMacAddress na estrutura de dados compartilhada de KDNET.

KdShutdownController

VOID

KdShutdownController (

    __in PVOID Adapter

    )

/*++
Routine Description:

    This function shuts down the Network controller.  No further packets can
    be sent or received until the controller is reinitialized.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

Return Value:

    None.
--*/

É FUNDAMENTAL que essa rotina AGUARDE até que todos os pacotes de transmissão que ainda estão pendentes sejam realmente transmitidos. Essa rotina precisa aguardar até que todos os pacotes de transmissão tenham sido enviados por DMA a partir da memória principal e sejam transmitidos ANTES de desligar a transmissão no hardware. Depois que todos os pacotes de transmissão pendentes forem enviados, essa rotina deverá desligar completamente o hardware. Essa rotina será chamada quando o sistema for desligado e também quando o sistema decidir gerenciar o transporte de depuração para um estado de baixa energia. Isso pode ser chamado quando o sistema entra em espera, hibernação, suspensão e espera conectada, além de quando o sistema é desligado.

KdGetHardwareContextSize

ULONG

KdGetHardwareContextSize (

    __in PDEBUG_DEVICE_DESCRIPTOR Device

    )
 

/*++
Routine Description:

    This function returns the required size of the hardware context in bytes.

Arguments:

    Device - Supplies a pointer to the debug device descriptor.

Return Value:

    None.

--*/

Essa rotina deve retornar o número de bytes necessários para toda a memória necessária para oferecer suporte ao hardware. Isso inclui sua estrutura de contexto e todos os buffers de pacotes para recebimento e transmissão, bem como os descritores de pacotes de hardware e outras estruturas. O tamanho de TODA a memória de que você necessita precisa ser relatado aqui, incluindo qualquer memória extra necessária para limitações de alinhamento que seu hardware possa ter para pacotes, descritores de pacotes ou outras estruturas.

Essa rotina deve ser chamada pela rotina KdInitializeLibrary quando ela define o campo Comprimento da Memória no descritor de dispositivo de depuração.

KdGetRxPacket

NTSTATUS

KdGetRxPacket (

    __in PVOID Adapter,

    __out PULONG Handle,

    __out PVOID *Packet,

    __out PULONG Length

)

/*++

Routine Description:

    This function returns the next available received packet to the caller.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

    Handle - Supplies a pointer to the handle for this packet.  This handle
        will be used to release the resources associated with this packet back
        to the hardware.

    Packet - Supplies a pointer that will be written with the address of the
        start of the packet.

    Length - Supplies a pointer that will be written with the length of the
        received packet.

Return Value:

    STATUS_SUCCESS when a packet has been received.
    STATUS_IO_TIMEOUT otherwise.

--*/

Essa rotina obtém o próximo pacote disponível que foi recebido, mas ainda não foi processado. Ela retorna um identificador para esse pacote. O identificador será usado para obter o endereço do pacote chamando KdGetPacketAddress, bem como o tamanho chamando KdGetPacketLength. O pacote e o identificador devem continuar disponíveis e válidos até que o pacote seja liberado chamando KdReleaseRxPacket. Essa rotina também retorna diretamente o endereço e o tamanho do pacote para o chamador.

Se nenhum pacote estiver disponível no momento, essa rotina DEVERÁ retornar imediatamente com STATUS_IO_TIMEOUT. Essa rotina NÃO DEVE aguardar o recebimento de um pacote. Os dois bits superiores de Handle são reservados. TRANSMIT_HANDLE e TRANSMIT_ASYNC devem ser claros.

KdReleaseRxPacket

VOID

KdReleaseRxPacket (

    __in PVOID Adapter,

    ULONG Handle

)

/*++

Routine Description:

    This function reclaims the hardware resources used for the packet
    associated with the passed Handle.  It reprograms the hardware to use those
    resources to receive another packet.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.
    Handle - Supplies the handle of the packet whose resources should be
        reclaimed to receive another packet.

Return Value:

    None.

--*/

Essa rotina libera os recursos associados ao pacote Handle de volta ao hardware para que possam ser usados para receber outro pacote. Cada chamada para KdGetRxPacket bem-sucedida será seguida por outra chamada para KdReleaseRxPacket com o identificador retornado de KdGetRxPacket. NÃO há nenhuma garantia de que KdReleaseRxPacket será chamado imediatamente depois que KdGetRxPacket tiver êxito. É possível que outra chamada KdGetRxPacket seja feita primeiro. No entanto, toda chamada KdGetRxPacket bem-sucedida terá seus recursos liberados com uma chamada KdReleaseRxPacket.

Essa rotina deve programar corretamente o hardware para que os recursos liberados possam ser usados para receber outro pacote.

KdGetTxPacket

NTSTATUS

KdGetTxPacket (

    __in PVOID Adapter,

    __out PULONG Handle

)

/*++

Routine Description:

    This function acquires the hardware resources needed to send a packet and
    returns a handle to those resources.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

    Handle - Supplies a pointer to the handle for the packet for which hardware
        resources have been reserved.

Return Value:

    STATUS_SUCCESS when hardware resources have been successfully reserved.
    STATUS_IO_TIMEOUT if the hardware resources could not be reserved.
    STATUS_INVALID_PARAMETER if an invalid Handle pointer or Adapter is passed.

--*/

Essa rotina obtém os próximos recursos de transmissão disponíveis e retorna um identificador para eles. Esse identificador será usado para chamar KdGetPacketAddress e KdGetPacketLength. O endereço do pacote retornado por KdGetPacketAddress será usado para gravar diretamente o conteúdo do pacote. O endereço do pacote deve ser o início do pacote, e o tamanho deve ser o número máximo de bytes que podem ser gravados no pacote. Se não houver recursos de hardware disponíveis, porque todos eles foram adquiridos e ainda não foram transmitidos, essa rotina deverá retornar STATUS_IO_TIMEOUT imediatamente.

TRANSMIT_HANDLE deve ser definido no identificador retornado. Observe que os dois bits superiores de Handle são reservados para os sinalizadores TRANSMIT_ASYNC e TRANSMIT_HANDLE.

KdSendTxPacket

NTSTATUS

KdSendTxPacket (

    __in PVOID Adapter,

    ULONG Handle,

    ULONG Length

)

/*++

Routine Description:

    This function sends the packet associated with the passed Handle out to the
    network.  It does not return until the packet has been sent.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

    Handle - Supplies the handle of the packet to send.

    Length - Supplies the length of the packet to send.

Return Value:

    STATUS_SUCCESS when a packet has been successfully sent.

    STATUS_IO_TIMEOUT if the packet could not be sent within 100ms.

    STATUS_INVALID_PARAMETER if an invalid Handle or Adapter is passed.

--*/

Essa rotina envia o pacote associado ao identificador passado para a conexão. O Handle pode ter um bit adicional definido nele, o qual indica se o envio é uma transferência assíncrona ou não. Se o sinalizador TRANSMIT_ASYNC estiver definido no identificador, essa rotina deverá programar o hardware para enviar o pacote e retornar imediatamente, sem esperar que o hardware conclua o envio. Isso significa que os erros que ocorrerem durante a transmissão serão perdidos. Tudo bem, e por design, pois os pacotes podem ser perdidos de qualquer maneira na transmissão. Se o sinalizador TRANSMIT_ASYNC não estiver definido no Handle, essa rotina DEVERÁ aguardar até que o pacote tenha sido enviado na transmissão e deve retornar qualquer erro que ocorra durante a transmissão, se houver. Observe que, quando os arquivos de despejo estiverem sendo enviados para o host do depurador, ou quando os pacotes de rede do Windows estiverem sendo enviados do KDNIC por meio do KDNET, TRANSMIT_ASYNC serão definidos. Quando todos os outros pacotes do depurador estiverem sendo enviados, TRANSMIT_ASYNC será claro.

Se um conjunto de pacotes for enviado com TRANSMIT_ASYNC definido como TRUE, seguido por um pacote que não tenha TRANSMIT_ASYNC definido, o hardware deverá aguardar até que o pacote sem o sinalizador definido seja realmente enviado, mesmo que isso signifique que ele tenha que esperar que os pacotes assíncronos anteriores também sejam enviados.

KdGetPacketAddress

PVOID

KdGetPacketAddress (

    __in PVOID Adapter,

    ULONG Handle

)

/*++

Routine Description:

    This function returns a pointer to the first byte of a packet associated
    with the passed handle.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

    Handle - Supplies a handle to the packet for which to return the
        starting address.

Return Value:

    Pointer to the first byte of the packet.

--*/

Essa rotina retorna um ponteiro para o primeiro byte do pacote associado ao Handle passado. Observe que o Handle terá o bit de TRANSMIT_HANDLE definido para os pacotes de transmissão e o bit TRANSMIT_HANDLE limpo para os pacotes de recebimento. O ponteiro retornado deve ser um endereço virtual do Windows que pode ser lido ou gravado pelo processador. Esse endereço deve estar dentro do bloco de memória reservado para o módulo de extensibilidade KDNET que é passado na estrutura de Memória do descritor de dispositivo de depuração. (Observe que o módulo de extensibilidade KDNET NUNCA deve usar mais do que o tamanho de memória solicitado em KdInitializeLibrary ao acessar essa memória. Qualquer memória adicional no fim do bloco é reservada para uso pelo KDNET e não deve ser tocada pelo módulo de extensibilidade KDNET.)

KdGetPacketLength

ULONG

KdGetPacketLength (

    __in PVOID Adapter,

    ULONG Handle

)

/*++

Routine Description:

    This function returns the length of the packet associated with the passed
    handle.

Arguments:

    Adapter - Supplies a pointer to the debug adapter object.

    Handle - Supplies a handle to the packet for which to return the
        length.

Return Value:

    The length of the packet.

--*/

Essa rotina retorna um tamanho em bytes do pacote associado ao Handle passado. Observe que o Handle terá o bit de TRANSMIT_HANDLE definido para os pacotes de transmissão e o bit TRANSMIT_HANDLE limpo para os pacotes de recebimento. Para pacotes de transmissão, esse tamanho deve ser o número máximo de bytes que podem ser gravados no pacote. Para pacotes de recebimento, esse tamanho deve ser o número real de bytes no pacote recebido.

Depurar módulos de extensibilidade KDNET

Para depurar um módulo de extensibilidade KDNET, você precisa executar os comandos bcdedit a seguir a partir de um prompt de comando elevado no computador de destino.

Primeiro, e o mais importante, você precisa executar os dois comandos a seguir para garantir que o Winload permita falhas de inicialização repetidas sem seguir um caminho de falha especial que interrompa o depurador e impeça a inicialização normal. Executar esses comandos permitirá que você reinicialize repetidamente o computador com novos bits e depure esses novos bits sem problemas.

Bcdedit -set {current} BootStatusPolicy IgnoreAllFailures

Bcdedit -set {current} RecoveryEnabled No

Supondo que você usará a depuração serial em com1 no computador de destino para depurar o módulo de extensibilidade, faça o seguinte.

bcdedit -dbgsettings serial debugport:1 baudrate:115200

Isso define o transporte de depuração padrão como serial em com1 em 115.200 bauds. Essas configurações também serão usadas para depuração de inicialização.

bcdedit -debug on

Isso permite a depuração do kernel.

bcdedit -bootdebug on

Isso permite a depuração de inicialização em winload.exe, que você usará para depurar na inicialização antecipada do kernel, incluindo seu módulo de extensibilidade KDNET.

bcdedit -set kerneldebugtype net

Isso força o tipo de depuração do kernel para rede, independentemente das configurações de transporte de depuração padrão. Isso fará com que winload.exe carregue kdnet.dll como o transporte de depuração do kernel.

bcdedit -set kernelbusparams b.d.f

Em que b é o número do barramento, d é o número do dispositivo e f é o número da função – tudo em decimal – do hardware para o qual você está escrevendo o módulo de extensibilidade KDNET. Esses números dependerão de qual slot PCI o hardware está localizado. Você pode encontrá-los localizando a cadeia de caracteres de localização na página de propriedades do dispositivo de rede no gerenciador de dispositivos do Windows. Abra o gerenciador de dispositivos do Windows, clique duas vezes em dispositivos de rede, encontre seu dispositivo, clique duas vezes nele e, na janela aberta, deve haver um campo Local: que contém o barramento, o dispositivo e a função do hardware no barramento PCI. Se você tiver um driver de barramento que faça com que essa informação seja mascarada, terá que determinar a localização de seus drivers, ou de alguma outra forma.

Isso força os busparams do kernel ao b.d.f, o qual força esse dispositivo específico a ser selecionado como o dispositivo de depuração do kernel.

bcdedit -set kernelhostip N

Em que N é determinado pela fórmula a seguir. Se o computador do depurador host tiver um endereço IPv4 de w.x.y.z, N = (w0x01000000) + (x0x00010000) + (y0x00000100) + (z0x00000001). N precisa ser especificado na linha de comando em decimal, não hexadecimal. Efetivamente, você pega cada byte do endereço IPv4 e o concatena (em hexadecimal) para criar um número de 32 bits em hexadecimal e, em seguida, converte isso em decimal.

bcdedit -set kernelport N

Em que N é 50000 ou alguma outra porta que não será bloqueada em sua rede interna.

Isso força o KDNET a usar a porta N como a porta de depuração de rede.

bcdedit -set kernelkey 1.2.3.4

Isso força a chave de depuração KDNET para 1.2.3.4. 1.2.3.4 não é uma chave segura ou exclusiva na chave de rede. Para manter o computador de destino seguro, os pacotes transmitidos entre o host e os computadores de destino devem ser criptografados. É altamente recomendável que você use uma chave de criptografia gerada automaticamente. Para obter mais informações, confira Configurar automaticamente a depuração do kernel de rede KDNET.

bcdedit -set kerneldhcp on

Isso força a configuração dhcp do kernel KDNET a ser ativada.

Execute o depurador no computador host do depurador com a seguinte linha de comando, supondo que você esteja usando com1 como sua porta de depuração serial no computador host:

windbg -d -k com:port=com1,baud=115200

Isso executará o depurador e fará com que ele interrompa quando o depurador de inicialização windbg se comunicar pela primeira vez com o computador host.

Em seguida, reinicialize o computador de destino executando

shutdown -r -t 0

Quando o depurador entrar no windbg, certifique-se de obter os símbolos carregados para winload (talvez seja necessário definir o .sympath e fazer um .reload). Em seguida, execute x winload!*deb*tra*. Um dos símbolos listados será algo como BdDebugTransitions.

Em seguida, execute ed winload!BdDebugTransitions 1, mas certifique-se de usar o nome de símbolo correto.

Em seguida, execute bu winload!blbdstop para definir um ponto de interrupção.

Em seguida, pressione g para acessar.

Você deve entrar em winload! BlBdStop.

Em seguida, execute os comandos a seguir.

bu nt!KdInitSystem

bu kdnet!KdInitialize

bu kdstub!KdInitializeLibrary

É bem provável que você usará kdstub ao definir pontos de interrupção no módulo de extensibilidade KDNET, se isso não funcionar, use

bu kd_YY_XXXX!KdInitializeLibrary

Em que YY é sua classe PCI e XXXX é sua PCI VID. (ou seja: use o nome do seu módulo de extensibilidade KDNET.)

Geralmente, no depurador, você precisará usar kdstub, em vez de usar o nome real do seu módulo de extensibilidade.

Em seguida, execute bl para listar os pontos de interrupção. Certifique-se de que os pontos de interrupção estejam no lugar correto (todos eles devem ter um "e" ao lado deles).

Em seguida, pressione g. Você deve clicar no ponto de interrupção nt!KdInitSystem.

Clique em g novamente, e você deve clicar em kdnet!KdInitialize.

Clique em g novamente e você deve clicar em um ponto de interrupção em seu próprio módulo em KdInitializeLibrary.

Em seguida, você pode definir um ponto de interrupção em sua rotina InitializeController, bem como em todas as outras rotinas, e percorrer seu código.

Depois de passar por KdInitializeLibrary, clique em g e, se você definir um ponto de interrupção em sua rotina InitializeController, isso será atingido em seguida.

Quando isso for concluído, certifique-se de ter pontos de interrupção definidos nas rotinas KdGetTxPacket, KdSendTxPacket, KdGetRxPacket, KdReleaseRxPacket, e clique em g novamente e essas rotinas serão executadas como parte da inicialização de rede feita pelo KDNET durante a inicialização.

Talvez seja necessário adicionar um código temporário às rotinas KdInitializeLibrary ou KdInitializeController para garantir que todas as rotinas sejam chamadas, de forma que você possa percorrer todo o seu código. (Por exemplo, KdShutdownController não será chamada durante a inicialização quando tudo funcionar normalmente, então você precisará chamá-la explicitamente a partir do código temporário para que você possa percorrê-lo e certificar-se de que está correto.)

Depois de passar por todo o seu código e ter certeza de que esteja correto, reinicialize o alvo, mas NÃO defina o sinalizador winload!BdDebugTransitions como true (deixe-o definido como padrão para zero).

Em seguida, execute também outra instância do depurador do kernel em seu computador do depurador host.

Windbg -d -k net:port=50000,key=1.2.3.4

Deixe o computador de destino inicializar. Ele deve conectar à outra instância do depurador do kernel pela rede.

Em seguida, execute comandos no depurador do kernel e certifique-se de que ele funcione e, em seguida, deixe o destino continuar inicializando, e certifique-se de que você possa interromper posteriormente e executar comandos.

Observação

Definir o sinalizador de transições de depuração em winload garante que o Windows NÃO INICIALIZARÁ. Se você tentar permitir que o Windows conclua a inicialização depois de definir esse sinalizador, o Windows simplesmente falhará ou travará. Se você quiser que o Windows inicialize com êxito, não poderá definir esse sinalizador de transições de depuração. Definir o sinalizador permite que você depure seu código e verifique se ele está correto, passando por ele no depurador, mas, finalmente, você precisará não definir o sinalizador para que possa verificar se a depuração funciona quando você inicializa normalmente. Isso significa que você não pode percorrer seu código ao inicializar o sistema normalmente e, na verdade, quando o Windows está sendo executado normalmente, com a depuração habilitada em seu hardware, seu módulo de extensibilidade KDNET não pode ser depurado. Qualquer tentativa de depurá-lo com o depurador do kernel fará com que o computador trave. (Você não pode definir pontos de interrupção no código que é executado nos caminhos de depuração do kernel, pois isso causa reentrada infinita, uma pilha queimada e uma reinicialização.)

Múltiplas funções físicas - 2PF

Além da extensibilidade KDNET, o KDNET tem suporte para a depuração do kernel usando várias PFs (Funções Físicas) nas NICs suportadas, particionando o espaço de configuração do PCI. Os fornecedores de placas de rede são incentivados a habilitar o suporte para esse recurso. Para obter mais informações, consulte Suporte para driver de rede miniporta KDNET 2PF do depurador.

Confira também

Como configurar a depuração automática do kernel de rede KDNET

Como configurar a depuração do kernel de rede KDNET manualmente

Suporte para driver de rede miniporta KDNET 2PF do depurador