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 através do uso de uma dll de módulo de extensibilidade de driver de hardware separada. 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 através de uma rede. Inicialmente foi usado para suportar a depuração do kernel com NICs Ethernet. Ele foi projetado para que a camada de suporte de hardware seja construída em um módulo separado do processamento de pacotes de rede e da camada de interface do kernel. Esta 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 através do uso de uma dll de módulo de extensibilidade de driver de hardware separada. É o objetivo de suportar toda a depuração do Windows através dos módulos de extensibilidade KDNET e KDNET. Todas as outras DLLs de transporte do kernel (kdcom.dll, kd1394.dll, kdusb.dll, etc.) serão eventualmente preteridas e removidas do Windows.

Existem dois tipos de interfaces que o KDNET usa para se comunicar com os módulos de extensibilidade do 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 suportar KDNET sobre hardware serial.

Os módulos de extensibilidade KDNET devem seguir requisitos muito rigorosos para funcionarem corretamente. Sendo utilizados na depuração do kernel, eles serão chamados e executados quando o sistema estiver adiando a execução de código adicional. Geralmente, todos os processadores no sistema estão bloqueados girando em um IPI, exceto o processador que está se comunicando com o aplicativo depurador em execução na máquina host através do transporte de depuração do kernel. Esse processador normalmente está sendo executado com interrupções completamente desativadas e está essencialmente girando no hardware de transporte de depuração aguardando 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. Também não têm importações explícitas. Os módulos de extensibilidade do KDNET são passados um ponteiro para uma estrutura com uma lista das rotinas que eles podem 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 serão suportados.

Se você despejar as importações e exportações de um módulo de extensibilidade KDNET usando link /dump /exports e link /dump /imports, verá que eles têm apenas uma exportação (KdInitializeLibrary) e nenhuma importação. Os módulos de extensibilidade do 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. KDNET então usa os ponteiros de função nessa estrutura para invocar o módulo de extensibilidade e realizar transferências de dados usando o hardware suportado pelo módulo. O KDNET determina se o módulo é um módulo baseado em pacotes ou 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 suportar 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 ativa quando entra no depurador do kernel e também bloqueia os outros processadores do sistema através de um IPI ao aceder ao depurador. Esses processadores serão liberados, somente quando o depurador do kernel em execução no host disser à máquina de destino para permitir que a execução continue. Como o kernel faz essa sincronização, os módulos de extensibilidade do KDNET não devem absolutamente usar nenhum spinlock, mutexes, gates ou qualquer outro mecanismo de sincronização do Windows em seu código. Eles devem ser escritos para programar diretamente seu respetivo hardware para enviar e receber pacotes e/ou bytes.

O código do módulo de extensibilidade KDNET deve ser o mais simples possível. Isso ajudará a garantir que ele seja o mais livre de bugs possível, já que a depuração do código do módulo de extensibilidade KDNET ao vivo em uma máquina atualmente não é possível 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 provocará a reinicialização da máquina devido a um estouro de pilha do kernel (que normalmente termina com uma falha dupla e reinicialização), ou causará um bloqueio, ou fará com que o transporte seja reinserido, o que na maioria dos casos resultará em funcionamento incorreto.

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

O 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 é para suportar hardware baseado em PCI, então ele deve ser nomeado kd_YY_XXXX.dll onde XXXX é o ID do fornecedor PCI do seu hardware em hex, e YY é a classe PCI para o seu hardware. Existem vários módulos de extensão KDNET que são incluídos no Windows e suportam hardware baseado em PCI. Por exemplo, o kd_02_8086.dllda Intel, o kd_02_14e4.dllda Broadcom e o kd_02_10ec.dllda Realtek. Você pode procurar identificadores de fornecedor PCI registrados em https://www.pcisig.com/membership/member-companies. Todos os módulos de extensibilidade KDNET baseados em PCI usam o VID do fornecedor do hardware que eles suportam em hexadecimal como os quatro últimos caracteres no nome do módulo. O código de classe para a maioria dos módulos in box é 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 PCI. Winload.exe constrói o nome dos módulos de extensibilidade KDNET baseados em PCI, lendo a classe de dispositivo PCI e o VID PCI do dispositivo de depuração selecionado a partir do 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 0x02 de rede, então você deve 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 formato hexadecimal. Este código também é encontrado e lido a partir do espaço de configuração 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 seu módulo será diferente. A convenção de nomenclatura para dispositivos de depuração da tabela DBG2 é kd_XXXX_YYYY.dll, onde XXXX é o PortType hexadecimal e YYYY é o PortSubtype hexadecimal, ambos a partir da entrada da tabela DBG2. Kd_8003_5143.dll é uma DLL da caixa de entrada para suportar um PortType net (0x8003) com um subtipo de 0x5143. Neste caso, 5143 é o VID PCI da Qualcomm, uma vez que serve para suportar o KDNET em controladores USB Qualcomm. Para entradas na tabela Net DBG2, o PortSubtype é definido como o VID PCI do fornecedor do hardware. Observe que você pode suportar dispositivos de tabela serial, USB e outros DBG2 usando esta convenção de nomenclatura. A seguir estão os valores PortType atualmente suportados em hex: 8000 para dispositivos seriais, 8001 para dispositivos 1394, 8002 para dispositivos USB, 8003 para dispositivos NET. Observe que os subtipos para dispositivos seriais e USB devem ser reservados com a Microsoft. A Microsoft mantém uma lista dos subtipos serial e USB alocados. Por favor, envie um e-mail para kdnet@microsoft.com para reservar um subtipo serial ou USB, se os tipos suportados existentes não funcionarem com o seu hardware.

Importações de Extensão KDNET

A seguir está a lista de rotinas que você pode chamar a partir de um módulo de extensibilidade KDNET. Note que todas essas rotinas são passadas para a rotina KdInitializeLibrary, e o cabeçalho kdnetextensibility.h vai remapear as chamadas normais para essas rotinas, direcionando-as através da tabela de importação. Seu código deve chamá-los através da tabela de importação, para que seu módulo não tenha importações. Você não pode chamar quaisquer outras rotinas que são exportadas pelo kernel, o HAL ou qualquer outro módulo do kernel. Você só pode chamar essas rotinas. Este conjunto de rotinas revelou-se suficiente para desenvolver todos os módulos de extensibilidade KDNET in box 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 e-mail para kdnet@microsoft.com explicar seu cenário, e quais rotinas adicionais você precisa e por quê. Observe que essa lista só será adicionada nos principais ciclos de lançamento do Windows, se for o caso. Observe que a maioria dessas rotinas corresponde diretamente às APIs do kernel do Windows que são suportadas pelo kernel ou pelo HAL. Uma ou duas são rotinas personalizadas apenas do KDNET.

É fundamental que incluas kdnetextensibility.h corretamente nos teus cabeçalhos para que o remapeamento correto de rotinas através da tabela de importação possa ocorrer. Se isso não for feito, seu módulo terá importações e não será suportado.

Rotinas de leitura e escrita em memória

As rotinas a seguir devem ser usadas para ler e gravar na memória de dispositivo mapeada. Estes têm a mesma convenção de chamada e estão mapeados para as 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 através dessas rotinas, pois elas garantem que as leituras e gravações não sejam reordenadas pelo processador. Observe que o msdn.microsoft.com documenta rotinas do Windows CE Compact 2013 que correspondem na convenção de chamada para essas rotinas. Infelizmente, parece que as rotinas do NT não estão documentadas, mas a convenção de chamada é a mesma.

Ler rotinas da porta de entrada/saída

As rotinas a seguir devem ser usadas para ler e gravar em portas de E/S do dispositivo. Estes têm a mesma convenção de chamada e são mapeados 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 através destas rotinas. Observe que o msdn.microsoft.com documenta rotinas do Windows CE Compact 2013 que correspondem na convenção de chamada para essas rotinas.

Rotinas adicionais

As seguintes rotinas adicionais podem ser chamadas, e devem ser chamadas normalmente com os parâmetros especificados. Observe que ao incluir corretamente o cabeçalho kdnetextensibility.h, irá remapear as chamadas de função através da tabela de importação de extensibilidade KDNET, resultando em nenhuma importação explícita em seu módulo, como é necessário para 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

    );

Observe que 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 deve precisar chamar KeBugCheckEx, KdMapPhysicalMemory64 e KdUnmapVirtualAddress. Por outro lado, essencialmente, todos os módulos de extensibilidade KDNET precisarão chamar KdGetPhysicalAddress para obter os endereços de memória física necessários para programar mecanismos DMA de dispositivos, 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 do KDNET

Segue-se 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 pacote ou um módulo de extensibilidade KDNET baseado em série. A seguir estão as exportações do módulo de extensibilidade KDNET de pacotes.

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 do KDNET. Essa rotina deve validar se a versão das tabelas de importação e exportação é esperada e suportada, e falhar se não. Ele deve fazer uma cópia da tabela de importação em sua própria memória global. Ele deve escrever as rotinas que exporta na estrutura apontada pelo campo Exportações da tabela de importação. Deve-se também definir o campo Comprimento da estrutura de memória que faz parte do ponteiro do descritor de dispositivo de depuração passado a esta rotina, com o número de bytes de memória necessários para suportar o 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 - (KdNetExtensibilityImports->FunctionCount != KDNET_EXT_IMPORTS) e 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 WDK usada para construir 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 mudar. Por exemplo, se a interface de extensibilidade KDNET adicionar uma nova função adicional, a contagem já não corresponderá. Para garantir que eles correspondam, use sempre a versão WDK que corresponde ao sistema operacional que hospeda a versão KDNET.

Personalizar a memória necessária

Observe que o dispositivo será preenchido com o hardware selecionado para o depurador. Essa rotina deve personalizar a quantidade de memória necessária com base no dispositivo, se necessário. Por exemplo, módulos de extensibilidade que suportam hardware 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

Observe que essa rotina será chamada tanto pelo winload quanto pelo KDNET durante a chamada do KdInitSystem. Observe que esta é a ÚNICA rotina exportada pelos módulos de extensibilidade do KDNET. É a única rotina colocada em um arquivo .def. Os módulos de extensibilidade KDNET têm exatamente uma exportação explícita – esta 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.
--*/

Esta rotina é chamada pelo sistema antes da hibernação para que ele possa registrar corretamente o código usado pelo módulo de extensibilidade KDNET com o sistema. Isso permite que o sistema gerencie adequadamente essa memória durante a hibernação e retome da hibernação. (A memória é salva tarde e carregada cedo, já que será chamada muito cedo 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. Ele é chamado quando o sistema é inicializado e sempre que o sistema é ativado de um estado de baixo consumo de energia para o qual é chamado 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 o surgimento do PHY e que o vínculo seja estabelecido. Observe que, se não houver um cabo conectado, essa rotina não deve parar indefinidamente. Esta rotina define a velocidade do link e duplex na estrutura de dados compartilhados do KDNET que é compartilhada entre o KDNET e este módulo de extensibilidade. Ele também grava o endereço MAC usado pelo hardware, no local apontado pelo TargetMacAddress na estrutura de dados compartilhados do 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 enviados no fio. Essa rotina precisa esperar até que todos os pacotes de transmissão tenham sido transferidos por DMA da memória principal e estejam no cabo antes de desativar a transmissão no hardware. Uma vez que todos os pacotes de transmissão pendentes tenham sido enviados, essa rotina deve 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 modo de 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 suportar seu hardware. Isso inclui sua estrutura de contexto e todos os buffers de pacotes para receber e transmitir, bem como quaisquer descritores de pacotes de hardware e outras estruturas. O tamanho da memória ALL que você precisa 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.

Observe que esta rotina deve ser chamada pela rotina KdInitializeLibrary ao definir o campo Comprimento de Memória no descritor do 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 processado. Ele retorna um identificador para esse pacote. O identificador será usado para obter o endereço do pacote chamando KdGetPacketAddress, bem como o comprimento chamando KdGetPacketLength. O pacote e o identificador devem permanecer disponíveis e válidos até que o pacote seja liberado chamando KdReleaseRxPacket. Essa rotina também retorna diretamente o endereço e o comprimento do pacote para o chamador.

Se nenhum pacote estiver disponível no momento, essa rotina DEVE retornar imediatamente com STATUS_IO_TIMEOUT. Esta rotina NÃO DEVE esperar que um pacote seja recebido. Observe que os 2 bits superiores do 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 identificador de pacote de volta para o hardware para que possam ser usados para receber outro pacote. Cada chamada para KdGetRxPacket que for bem-sucedida será seguida por outra chamada para KdReleaseRxPacket com o identificador retornado de KdGetRxPacket. Observe que NÃO é garantido que KdReleaseRxPacket será chamado imediatamente após KdGetRxPacket ser bem-sucedido. É possível que outra invocação de KdGetRxPacket seja feita primeiro. No entanto, cada 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 esses recursos. 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 comprimento deve ser o número máximo de bytes que podem ser gravados no pacote. Observe que, se não houver recursos de hardware disponíveis, porque todos eles foram adquiridos e ainda não foram transmitidos, então essa rotina deve retornar imediatamente STATUS_IO_TIMEOUT.

TRANSMIT_HANDLE deve ser definido na alça retornada. 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.

--*/

Esta rotina envia o pacote associado ao manípulo passado para o cabo. Observe que Handle pode ter um bit adicional definido, que indica se o envio é uma transferência assíncrona ou não. Se o sinalizador de TRANSMIT_ASYNC estiver definido na alça, essa rotina deve programar o hardware para enviar o pacote e, em seguida, deve retornar imediatamente sem esperar que o hardware conclua o envio. Isto significa que quaisquer erros que ocorram durante a transmissão serão perdidos. Isso está bem, e intencionalmente, já que pacotes podem ser perdidos na rede de qualquer maneira. Se o sinalizador TRANSMIT_ASYNC não estiver definido no Handle, essa rotina DEVE aguardar até que o pacote tenha sido enviado no fio e deve retornar qualquer erro que ocorra durante a transmissão, se houver. Note que quando os ficheiros de despejo estão a ser enviados para o host do depurador, ou quando pacotes de rede do Windows estão a ser enviados do KDNIC através do KDNET, o TRANSMIT_ASYNC será definido. Quando todos os outros pacotes do depurador estiverem sendo enviados, TRANSMIT_ASYNC ficará 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 deve esperar 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 sejam enviados também.

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 TRANSMIT_HANDLE bit definido para pacotes de transmissão e o TRANSMIT_HANDLE bit limpo para pacotes de recebimento. O ponteiro retornado deve ser um endereço virtual do Windows que possa ser lido ou escrito pelo processador. Este 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 do dispositivo de depuração. (Observe que o módulo de extensibilidade KDNET NUNCA deve usar mais do que o tamanho de memória solicitado no KdInitializeLibrary ao acessar essa memória. Qualquer memória adicional no final do bloco é reservada para uso pelo KDNET e não deve ser tocada pelo módulo de extensibilidade do 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 comprimento em bytes do pacote associado ao Handle passado. Observe que o Handle terá o TRANSMIT_HANDLE bit definido para pacotes de transmissão e o TRANSMIT_HANDLE bit limpo para pacotes de recebimento. Para pacotes de transmissão, esse comprimento deve ser o número máximo de bytes que podem ser gravados no pacote. Para pacotes de recebimento, esse comprimento deve ser o número real de bytes no pacote recebido.

Desenvolvimento de Módulos de Extensibilidade do KDNET

Para depurar um módulo de extensibilidade KDNET, você precisa executar os seguintes comandos bcdedit a partir de um prompt de comando elevado na máquina de destino.

Primeiro, e mais importante, você precisa executar os dois comandos a seguir para garantir que o Winload permita falhas de inicialização repetidas sem seguir por um caminho de falha especial que invada o depurador e impeça a inicialização normal. A execução desses comandos permitirá que você reinicie repetidamente a máquina com novos bits e depure esses novos bits sem problemas.

Bcdedit -set {current} BootStatusPolicy IgnoreAllFailures

Bcdedit -set {current} RecoveryEnabled No

Supondo que irá utilizar a depuração serial em com1 na máquina de destino para depurar o módulo de extensibilidade, execute o seguinte.

bcdedit -dbgsettings serial debugport:1 baudrate:115200

Isso define o transporte de depuração padrão como serial em COM1 a 115200 baud. 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 no winload.exe, que você usará para depurar o início da inicialização do kernel, incluindo o seu módulo de extensibilidade KDNET.

bcdedit -set kerneldebugtype net

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

bcdedit -set kernelbusparams b.d.f

Onde 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 localização na seção de propriedades do dispositivo de rede no Gerenciador de Dispositivos do Windows. Abra o Gestor de Dispositivos do Windows, faça duplo clique em dispositivos de rede, encontre o seu dispositivo, faça duplo clique nele, e na janela que se abre deve haver um campo Localização: que contém o barramento, o dispositivo e a função do hardware no barramento PCI. Se você tem um motorista de ônibus que faz com que essa informação seja mascarada, então você terá que determinar a localização de seus motoristas, ou de alguma outra forma.

Isso força os busparams do kernel a b.d.f – o que 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 seguinte fórmula. Se sua máquina depuradora host tiver um endereço IPv4 de w.x.y.z, então N = (w0x01000000) + (x0x00010000) + (y0x00000100) + (z0x00000001). N precisa ser especificado na linha de comando em decimal, não hex. Efetivamente você pega cada byte do endereço IPv4 e o concatena (em hex) para criar um número de 32 bits em hex e, em seguida, converte isso em decimal.

bcdedit -set kernelport N

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

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

bcdedit -set kernelkey 1.2.3.4

Isso força a chave de depuração do KDNET para 1.2.3.4. 1.2.3.4 não é uma chave de rede segura ou única. Para manter o computador de destino seguro, os pacotes que viajam 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, consulte Configurando a depuração do kernel da rede KDNET automaticamente.

bcdedit -set kerneldhcp on

Isso força a configuração dhcp do kernel KDNET a estar ativa.

Execute o depurador na máquina host do depurador com a seguinte linha de comando, supondo que você esteja usando com1 como sua porta de depuração serial na máquina 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 do WinDbg se comunicar pela primeira vez com a máquina host.

Em seguida, reinicie a máquina de destino executando

shutdown -r -t 0

Quando o depurador entrar no windbg, certifique-se de que os símbolos para winload estejam carregados. (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 do símbolo correto.

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

Então pressione g para ir.

Você deve entrar no winload! BlBdStop.

Em seguida, execute os seguintes comandos.

bu nt!KdInitSystem

bu kdnet!KdInitialize

bu kdstub!KdInitializeLibrary

Observe que provavelmente você usará kdstub ao definir pontos de interrupção no seu módulo de extensibilidade KDNET, se isso não funcionar, use

bu kd_YY_XXXX!KdInitializeLibrary

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

Normalmente, 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 paragem estão definidos corretamente (devem todos ter um "e" ao lado deles).

Em seguida, clique em g. Você deve atingir o nt!KdInitSystem breakpoint.

Bata g novamente, e você deve bater kdnet! KdInitialize

Pressione g novamente e você deve atingir 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 todas as suas outras rotinas, e percorrer seu código.

Depois de percorrer KdInitializeLibrary, ative g e, se definires um ponto de interrupção na rotina InitializeController, este será acionado em seguida.

Depois que isso for concluído, certifique-se de ter pontos de interrupção definidos em suas rotinas KdGetTxPacket, KdSendTxPacket, KdGetRxPacket, KdReleaseRxPacket e pressione 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 código temporário às rotinas KdInitializeLibrary ou KdInitializeController para garantir que todas as suas rotinas sejam chamadas para que você possa percorrer todo o código. (KdShutdownController, por exemplo, não será chamado durante a inicialização quando as coisas funcionarem normalmente, então você precisará chamá-lo explicitamente do código temporário para que possa percorrê-lo e certificar-se de que está correto.)

Depois de ter passado por todo o seu código e estiver confiante de que está correto, reinicie o alvo, mas NÃO defina o winload! BdDebugTransitions sinalizador para true (deixe-o padrão para zero).

Em seguida, execute também outra instância do depurador do kernel na máquina do depurador do host.

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

Deixe a máquina de destino inicializar e ela deve se conectar à outra instância do depurador do kernel pela rede.

Em seguida, execute comandos no depurador do kernel e certifique-se de que estão a funcionar. Depois, deixe o sistema alvo continuar a inicialização e certifique-se de que pode, posteriormente, interromper o processo e executar comandos.

Observação

Definir o sinalizador de transições de depuração no winload garante que o Windows NÃO IRÁ INICIALIZAR. Se você tentar permitir que o Windows termine 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, em última análise, 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 depurar passo a passo o seu código ao inicializar o sistema normalmente e, de fato, quando o Windows está funcionando normalmente, com a depuração ativada em seu hardware, o seu módulo de extensibilidade KDNET não pode ser depurado. Qualquer tentativa de utilizar o depurador do kernel para depurá-lo fará com que a máquina trave. (Não é possível definir pontos de interrupção no código executado nos caminhos de depuração do kernel, pois isso causa infinita reentrada, um estouro de pilha e uma reinicialização.)

Múltiplas Funções Físicas - 2PF

Além da extensibilidade do KDNET, o KDNET suporta depuração do kernel usando várias funções físicas (PFs) nas NICs suportadas, particionando o espaço de configuração PCI. Os fornecedores de placas de rede são incentivados a habilitar o suporte para esse recurso. Para obter mais informações, consulte Debugger 2PF KDNET Miniport Network Driver Support.

Ver também

Configuração automática da depuração do kernel na rede KDNET

Configurando a depuração do kernel de rede KDNET manualmente

Suporte ao Driver de Rede Miniport do Depurador 2PF KDNET