Desarrollo de módulos de extensibilidad de transporte KDNET
En este tema se describe cómo se puede extender el transporte KDNET para ejecutarse en cualquier hardware mediante el uso de un archivo DLL de módulo de extensibilidad de controladores de hardware independiente. Los proveedores de tarjetas de red desarrollan módulos de extensibilidad de transporte KDNET para agregar compatibilidad de depuración de kernel a tarjetas de red específicas.
Introducción a KDNET
KDNET es un transporte de depuración de kernel que permite la depuración de kernel de Windows a través de una red. Inicialmente se usó para admitir la depuración del kernel con NIC Ethernet. Está diseñado para que la capa de compatibilidad de hardware esté integrada en un módulo independiente del procesamiento de paquetes de red y la capa de interfaz de kernel. Esta capa de compatibilidad con controladores de hardware se denomina módulo de extensibilidad KDNET.
KDNET es el único transporte que se puede extender para ejecutarse en cualquier hardware mediante el uso de un archivo DLL de módulo de extensibilidad de controladores de hardware independiente. Es el objetivo admitir toda la depuración de Windows a través de los módulos de extensibilidad KDNET y KDNET. El resto de archivos DLL de transporte de kernel (kdcom.dll, kd1394.dll, kdusb.dll, etc.) quedarán en desuso y se quitarán de Windows.
Hay dos tipos de interfaces que KDNET usa para comunicarse con módulos de extensibilidad KDNET. Una es una interfaz basada en paquetes que se usa para NIC, USB y hardware inalámbrico, y la otra es una interfaz basada en bytes que se usa para admitir KDNET a través del hardware serie.
Los módulos de extensibilidad de KDNET deben cumplir requisitos muy estrictos para poder funcionar correctamente. Dado que se usan para la depuración de kernel, se llamarán y ejecutarán cuando el sistema mantenga desactivada la ejecución posterior del código. Por lo general, todos los procesadores del sistema están bloqueados girando en un IPI, excepto para el procesador que se comunica con la aplicación del depurador, que se ejecuta en la máquina host a través del transporte de depuración del kernel. Normalmente, ese procesador se ejecuta con interrupciones completamente deshabilitadas y básicamente gira en el hardware de transporte de depuración esperando que los comandos provengan del depurador.
Importación y exportación
Los módulos de extensibilidad de KDNET tienen exactamente una exportación explícita: KdInitializeLibrary. Tampoco tienen importaciones explícitas. A los módulos de extensibilidad de KDNET se les pasa un puntero a una estructura con una lista de las rutinas a las que KDNET puede llamar cuando llama a KdInitializeLibrary. No se puede llamar a ninguna otra rutina. Y listo. Los módulos de extensibilidad de KDNET que tienen importaciones están diseñados incorrectamente y no se admitirán.
Si vuelva las importaciones y exportaciones de módulos de extensibilidad de KDNET mediante link /dump /exports y link /dump /imports, verá que solo tienen una exportación (KdInitializeLibrary) y ninguna importación. Los módulos de extensibilidad de KDNET notifican sus exportaciones adicionales a KDNET rellenando punteros de función en una estructura de funciones de exportación a la que KDNET pasa un puntero cuando se llama a KdInitializeLibrary. A continuación, KDNET usa los punteros de función de esa estructura para llamar al módulo de extensibilidad y efectuar transferencias de datos mediante el hardware admitido por el módulo. KDNET determina si el módulo es un módulo basado en paquetes o basado en bytes examinando qué funciones específicas rellena el módulo en la tabla de funciones de exportación de la estructura. Algunas de esas funciones son para admitir hardware basado en paquetes, y otras son para hardware basado en serie. Algunas de las funciones de la tabla se usan en hardware basado en paquetes y serie (KdInitializeController, KdShutdownController, KdGetHardwareContextSize).
Diseño de código
Los módulos de extensibilidad de KDNET deben escribirse como código de subproceso único. No deben realizar ninguna sincronización. Todos los transportes de depuración de kernel dependen del kernel de Windows para realizar la sincronización adecuada cuando se accede al depurador. El kernel tiene un bloqueo del depurador que toma cuando entra en el depurador del kernel y también bloquea los demás procesadores del sistema en un IPI cuando se accede al depurador. Esos procesadores se liberarán solo cuando el depurador de kernel que se ejecuta en el host indique al equipo de destino que permita que la ejecución continúe. Dado que el kernel realiza esta sincronización, los módulos de extensibilidad de KDNET no deben usar bajo ningún concepto ningún interbloqueo, exclusión mutua, puertas ni otros mecanismos de sincronización de Windows en su código. Deben escribirse para programar directamente su hardware respectivo para enviar y recibir paquetes y bytes.
El código del módulo de extensibilidad de KDNET debe ser lo más sencillo posible. Esto ayudará a garantizar que esté lo más libre de errores posible, ya que la depuración del código del módulo de extensibilidad KDNET en directo en una máquina no es posible actualmente sin el uso de un depurador de hardware. No puede usar el depurador de kernel para depurar el código de transporte de depuración del kernel. Intentarlo provocará que la máquina se reinicie debido a una pila del kernel estropeada (que normalmente termina con un doble fallo y reinicio), o un interbloqueo, o provocará que se vuelva a entrar en el transporte, lo que en la mayoría de los casos provocará que el transporte no funcione correctamente.
Convenciones de nomenclatura de módulos de extensibilidad de KDNET
El módulo de transporte de depuración del kernel debe seguir una de estas dos convenciones de nomenclatura para los módulos de extensibilidad de KDNET. Si el módulo es para admitir hardware basado en PCI, debe denominarse kd_YY_XXXX.dll, donde XXXX es el identificador de proveedor de PCI del hardware en valor hexadecimal e YY es la clase de PCI para el hardware. Hay varios módulos de extensibilidad KDNET incluidos en Windows que admiten hardware basado en PCI. Por ejemplo, kd_02_8086.dll de Intel, kd_02_14e4.dll de Broadcom y kd_02_10ec.dll de Realtek. Puede buscar identificadores de proveedor de PCI registrados en https://www.pcisig.com/membership/member-companies Todos los módulos de extensibilidad KDNET basados en PCI que usan el VID del proveedor del hardware que se admiten en valor hexadecimal como los últimos 4 caracteres en el nombre de su módulo. El código de clase para la mayoría de los módulos incluidos es 02, ya que son dispositivos de clase de red y, por tanto, tienen una clase de PCI de 0x02 en su espacio de configuración de PCI. Winload.exe compila el nombre de los módulos de extensibilidad KDNET basados en PCI mediante la lectura de la clase de dispositivo PCI y el VID de PCI del dispositivo de depuración seleccionado desde su espacio de configuración de PCI e intenta cargar un módulo con esos identificadores en el nombre. Si el dispositivo tiene un código de clase PCI que no es la clase 0x02 de red, debe usar el código de clase PCI correcto en valor hexadecimal para el dispositivo, en el nombre del módulo de extensibilidad KDNET. De lo contrario, winload no cargará correctamente el módulo. _02_
en cada uno de esos nombres es el código de clase PCI para los dispositivos de clase de red en valor hexadecimal. Este código también se encuentra y lee del espacio de configuración PCI del dispositivo de depuración.
Si tiene un dispositivo que tiene una entrada de tabla DBG2 y no es un dispositivo basado en PCI, la convención de nomenclatura del módulo es diferente. La convención de nomenclatura de los dispositivos de depuración de la tabla DBG2 es kd_XXXX_YYYY.dll, donde XXXX es la tabla hexadecimal DBG2 PortType e YYYY es la tabla hexadecimal DBG2 PortSubtype de la entrada de la tabla DBG2. Kd_8003_5143.dll es un archivo DLL de bandeja de entrada para admitir un PortType de red (0x8003) con un subtipo de 0x5143. En este caso, 5143 es el VID de PCI de Qualcomm, para admitir KDNET en controladores USB Qualcomm, y para entradas de tabla de de red DBG2 se define PortSubtype como el VID de PCI para el proveedor del hardware. Tenga en cuenta que puede admitir dispositivos de tabla serie, USB y otros de DBG2 mediante esta convención de nomenclatura. Estos son los valores de PortType admitidos actualmente en valor hexadecimal: 8000 para dispositivos serie, 8001 para dispositivos 1394, 8002 para dispositivos USB, 8003 para dispositivos NET. Tenga en cuenta que los subtipos para dispositivos serie y USB deben reservarse con Microsoft. Microsoft mantiene una lista de los subtipos de serie y USB asignados. Envíe un correo electrónico a kdnet@microsoft.com para reservar un subtipo serie o USB, si los tipos admitidos existentes no funcionan con el hardware.
Importaciones de extensibilidad de KDNET
A continuación se muestra la lista de rutinas a las que puede llamar desde un módulo de extensibilidad KDNET. Tenga en cuenta que todas estas rutinas se pasan a la rutina KdInitializeLibrary y el encabezado kdnetextensibility.h reasignará las llamadas normales a estas rutinas para pasar por la tabla de importación. El código debe realizar las llamadas a través de la tabla de importación, de modo que el módulo no tenga importaciones. No puede llamar a ninguna otra rutina que exporte el kernel, el HAL ni ningún otro módulo de kernel. Solo puede llamar a estas rutinas. Este conjunto de rutinas ha demostrado ser suficiente para desarrollar todos los módulos de extensibilidad KDNET incluidos y debe ser suficiente para escenarios normales. Si necesita rutinas adicionales a las que exporta el kernel, pero no están en esta lista, envíe un correo electrónico a kdnet@microsoft.com para explicar su caso y para indicar qué rutinas adicionales necesita y por qué. Tenga en cuenta que esta lista solo se agregará, si acaso, en los ciclos de versión principales de Windows. Tenga en cuenta que la mayoría de estas rutinas corresponden directamente a las API de kernel de Windows compatibles con el kernel o HAL. Una o dos son rutinas de KDNET personalizadas.
Es fundamental incluir kdnetextensibility.h correctamente en los encabezados para que se pueda producir la reasignación correcta de rutinas a través de la tabla de importación. Si no se hace, el módulo tendrá importaciones y no se admitirá.
Leer rutinas de memoria de escritura
Las siguientes rutinas deben usarse para leer y escribir en la memoria del dispositivo asignado. Tienen la misma convención de llamada y se asignan a sus rutinas de kernel correspondientes: READ_REGISTER_UCHAR, READ_REGISTER_USHORT, READ_REGISTER_ULONG, WRITE_REGISTER_UCHAR, WRITE_REGISTER_USHORT, WRITE_REGISTER_ULONG y en plataformas de 64 bits solo READ_REGISTER_ULONG64 y WRITE_REGISTER_ULONG64. Todo el acceso a la memoria del dispositivo debe realizarse a través de estas rutinas, ya que garantizan que el procesador no reordene las lecturas y escrituras. Tenga en cuenta que en msdn.microsoft.com se documentan las rutinas de Windows CE Compact 2013 que corresponden a la convención de llamada a estas rutinas. Desafortunadamente, parece que las rutinas NT no están documentadas, pero la convención de llamadas es la misma.
Leer rutinas de puerto de E/S
Las siguientes rutinas deben usarse para leer y escribir en puertos de E/S del dispositivo. Tienen la misma convención de llamada y se asignan a sus rutinas de kernel correspondientes: READ_PORT_UCHAR, READ_PORT_USHORT, READ_PORT_ULONG, WRITE_PORT_UCHAR, WRITE_PORT_USHORT y WRITE_PORT_ULONG. Todo el acceso al puerto de E/S del dispositivo debe realizarse a través de estas rutinas. Tenga en cuenta que en msdn.microsoft.com se documentan las rutinas de Windows CE Compact 2013 que corresponden a la convención de llamada a estas rutinas.
Rutinas adicionales
Se puede llamar a las siguientes rutinas adicionales, normalmente con los parámetros especificados. Tenga en cuenta que, al incluir correctamente el encabezado kdnetextensibility.h, volverá a asignar las llamadas de función a través de la tabla de importación de extensibilidad KDNET, lo que provocará que no haya importaciones explícitas en el módulo, tal como se requiere para los módulos de extensibilidad de 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
);
Tenga en cuenta que solo se debe llamar a la función PoSetHiberRange desde la rutina KdSetHibernateRange. Además, la mayoría de los módulos de extensibilidad de KDNET no deben necesitar llamar a KeBugCheckEx, KdMapPhysicalMemory64 ni KdUnmapVirtualAddress. Por otro lado, básicamente, todos los módulos de extensibilidad KDNET tendrán que llamar a KdGetPhysicalAddress para obtener direcciones de memoria física necesarias para programar motores DMA del dispositivo, y muchos tendrán que llamar a KeStallExecutionProcessor, KdGetPciDataByOffset y KdSetPciDataByOffset. Las dos últimas rutinas son para acceder al espacio de configuración PCI del dispositivo.
Exportaciones de extensibilidad de KDNET
A continuación se muestra una breve descripción de cada una de las rutinas de extensibilidad de KDNET. Debe implementar todas las rutinas necesarias para un módulo de extensibilidad KDNET basado en paquetes o un módulo de extensibilidad KDNET basado en serie. A continuación se muestran las exportaciones del módulo KDNET de extensibilidad de paquetes.
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;
}
Se llama a esta rutina para pasar las rutinas de importación y exportación entre KDNET y este módulo de extensibilidad de KDNET. Esta rutina debe validar que la versión de las tablas de importación y exportación es la esperada y admitida, y fallar en caso contrario. Debe realizar una copia de la tabla de importación en su propia memoria global. Debe escribir las rutinas a las que exporta en la estructura a la que apunta el campo Exports de la tabla de importación. También debe establecer el campo Length de la estructura Memory que forma parte del puntero descriptor del dispositivo de depuración pasado a esta rutina, con el número de bytes de memoria que requiere para admitir el dispositivo de hardware.
Validar el número de exportación de importación
El código debe comprobar que Imports FunctionCount coincide con lo que está disponible en el sistema operativo, por ejemplo (KdNetExtensibilityImports->FunctionCount != KDNET_EXT_IMPORTS)
, y devolver STATUS_INVALID_PARAMETER
si el recuento no coincide.
La comprobación de los recuentos garantiza la compatibilidad entre la versión de KDNET del sistema operativo Windows en ejecución (por ejemplo, el cargador de arranque,el cargador del sistema operativo o el hipervisor/kernel seguro/NT OS, todos los cuales se vinculan a la biblioteca KDNET) y la versión de WDK usada para compilar el módulo de extensibilidad KDNET actual. El WDK y la versión del sistema operativo deben sincronizarse; De lo contrario, se producirá un error en la comprobación anterior si cambia el valor del contador de importación/exportación. Por ejemplo, si la interfaz de extensibilidad de KDNET agregó una nueva función de característica, el recuento ya no coincidirá. Para asegurarse de que coinciden, use siempre la versión de WDK que coincida con el sistema operativo que hospeda la versión de KDNET.
Personalización de la memoria necesaria
Tenga en cuenta que el dispositivo se rellenará con el hardware seleccionado para el depurador. Esta rutina debe personalizar la cantidad de memoria necesaria en función del dispositivo, si es necesario. Por ejemplo, los módulos de extensibilidad que admiten hardware de 1 GB y 10 GB pueden aumentar el tamaño de memoria que solicitan para dispositivos de 10 GB. Pueden determinar qué dispositivo se usa examinando el campo DeviceID del descriptor de dispositivo de depuración.
KdInitializeLibrary es la única exportación
Tenga en cuenta que winload y KDNET llamarán a esta rutina durante la llamada a KdInitSystem. Tenga en cuenta que esta es la única rutina que exportan los módulos de extensibilidad KDNET. Es la única rutina colocada en un archivo .def. Los módulos de extensibilidad de KDNET tienen exactamente una exportación explícita (esta rutina) y no tienen importaciones.
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.
--*/
El sistema llama a esta rutina antes de hibernar para que pueda registrar correctamente el código usado por el módulo de extensibilidad KDNET con el sistema. Esto permite que el sistema administre correctamente esa memoria durante la hibernación y después se reanude. (La memoria se guarda tarde y se carga pronto, ya que se llamará muy pronto durante la reanudación).
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.
--*/
Se llama a esta rutina para inicializar el hardware. Se llama cuando el sistema se inicializa y siempre que el sistema se reactiva desde un estado de baja energía para el que llamó a KdShutdownController. Esta rutina debe asegurarse de que el hardware ha completado completamente la inicialización y que está listo para enviar paquetes antes de volver. Esta rutina debe esperar a que aparezca el PHY y que se establezca el vínculo. Tenga en cuenta que si no hay ningún cable conectado, esta rutina no debe detenerse indefinidamente. Esta rutina establece la velocidad del vínculo y el dúplex en la estructura de datos compartidos de KDNET que se comparte entre KDNET y este módulo de extensibilidad. También escribe la dirección MAC usada por el hardware, en la ubicación a la que apunta TargetMacAddress en la estructura de datos compartidos 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.
--*/
Es fundamental que esta rutina espere hasta que todos los paquetes de transmisión que todavía están pendientes se envíen en la conexión. Esta rutina debe esperar hasta que haya finalizado la operación de DMA en todos los paquetes de transmisión de la memoria principal y estén fuera de la conexión antes de apagar la transmisión en el hardware. Una vez enviados todos los paquetes de transmisión pendientes, esta rutina debe apagar completamente el hardware. Se llamará a esta rutina cuando el sistema se apague y también cuando el sistema decida administrar el transporte de depuración a un estado de baja energía. Se puede llamar cuando el sistema entra en espera, hibernación, suspensión y modo de espera conectado, y también cuando el sistema está apagado.
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.
--*/
Esta rutina debe devolver el número de bytes necesarios para toda la memoria necesaria para admitir el hardware. Esto incluye la estructura de contexto y todos los búferes de paquetes para recibir y transmitir, así como los descriptores de paquetes de hardware y otras estructuras. El tamaño de toda la memoria que necesita debe notificarse aquí. Incluya también cualquier memoria adicional necesaria para las limitaciones de alineación que el hardware pueda tener para paquetes, descriptores de paquetes u otras estructuras.
Tenga en cuenta que la rutina KdInitializeLibrary debe llamar a esta rutina cuando establezca el campo Length de Memory en el descriptor de dispositivo de depuración.
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.
--*/
Esta rutina obtiene el siguiente paquete disponible que se ha recibido, pero que aún no se ha procesado. Devuelve un identificador para ese paquete. El identificador se usará para obtener la dirección del paquete llamando a KdGetPacketAddress, así como la longitud llamando a KdGetPacketLength. El paquete y el identificador deben permanecer disponibles y válidos hasta que el paquete se libere llamando a KdReleaseRxPacket. Esta rutina también devuelve directamente la dirección y la longitud del paquete al llamador.
Si no hay ningún paquete disponible actualmente, esta rutina debe devolverse inmediatamente con STATUS_IO_TIMEOUT. Esta rutina no debe esperar a que se reciba un paquete. Tenga en cuenta que los 2 bits principales de Handle están reservados. TRANSMIT_HANDLE y TRANSMIT_ASYNC deben estar sin seleccionar.
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.
--*/
Esta rutina libera los recursos asociados con el identificador de paquete al hardware para que se puedan usar para recibir otro paquete. Cada llamada a KdGetRxPacket que se realice correctamente irá seguida de otra llamada a KdReleaseRxPacket con el identificador devuelto por KdGetRxPacket. Tenga en cuenta que no se garantiza que se llame a KdReleaseRxPacket inmediatamente después de que KdGetRxPacket se realice correctamente. Es posible que se realice primero otra llamada a KdGetRxPacket. Sin embargo, cada llamada a KdGetRxPacket correcta hará que sus recursos se liberen con una llamada a KdReleaseRxPacket.
Esta rutina debe programar correctamente el hardware para que los recursos liberados se puedan usar para recibir otro paquete.
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.
--*/
Esta rutina obtiene los siguientes recursos de transmisión disponibles y devuelve un identificador a los mismos. Este identificador se usará para llamar a KdGetPacketAddress y KdGetPacketLength. La dirección de paquete devuelta por KdGetPacketAddress se usará para escribir directamente el contenido del paquete. La dirección del paquete debe ser el inicio del paquete y la longitud debe ser el número máximo de bytes que se pueden escribir en el paquete. Tenga en cuenta que si no hay recursos de hardware disponibles, ya que todos se han adquirido y aún no se han transmitido, esta rutina debe devolver inmediatamente STATUS_IO_TIMEOUT.
TRANSMIT_HANDLE debe establecerse en el identificador devuelto. Tenga en cuenta que los dos bits superiores de Handle están reservados para las marcas TRANSMIT_ASYNC y 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 rutina envía el paquete asociado al controlador pasado a la conexión. Tenga en cuenta que Handle puede tener un bit adicional establecido, que indica si el envío es una transferencia asincrónica o no. Si la marca TRANSMIT_ASYNC está establecida en el identificador, esta rutina debe programar el hardware para enviar el paquete y, a continuación, debe devolver inmediatamente sin esperar a que el hardware complete el envío. Esto significa que se perderán los errores que se produzcan durante la transmisión. Esto es correcto y por diseño, ya que los paquetes se pueden perder en la conexión de todos modos. Si la marca TRANSMIT_ASYNC no está establecida en el identificador, esta rutina debe esperar hasta que el paquete se haya enviado fuera de conexión y debe devolver cualquier error que se produzca durante la transmisión, si lo hay. Tenga en cuenta que cuando se envían archivos de volcado al host del depurador o cuando se envían paquetes de red de Windows desde KDNIC a través de KDNET, se establecerá TRANSMIT_ASYNC. Cuando se envíen todos los demás paquetes del depurador, TRANSMIT_ASYNC se borrará.
Si se envía un conjunto de paquetes con TRANSMIT_ASYNC establecido en TRUE, seguido de un paquete que no tiene TRANSMIT_ASYNC establecido, el hardware debe esperar hasta que se envíe el paquete sin la marca establecida, incluso si esto significa tener que esperar a que también se envíen los paquetes asincrónicos anteriores.
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.
--*/
Esta rutina devuelve un puntero al primer byte del paquete asociado al identificador pasado. Tenga en cuenta que el identificador tendrá el bit de TRANSMIT_HANDLE establecido para los paquetes de transmisión y el bit TRANSMIT_HANDLE borrado para los paquetes de recepción. El puntero devuelto debe ser una dirección virtual de Windows que el procesador pueda leer o escribir. Esta dirección debe estar dentro del bloque de memoria reservado para el módulo de extensibilidad KDNET que se pasa en la estructura Memory del descriptor del dispositivo de depuración. (Tenga en cuenta que el módulo de extensibilidad KDNET nunca debe usar más del tamaño de memoria que solicitó en KdInitializeLibrary al acceder a esa memoria. KDNET reserva cualquier memoria adicional al final del bloque para que la use KDNET y no debe tocarla el módulo de extensibilidad 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.
--*/
Esta rutina devuelve una longitud en bytes del paquete asociado al identificador pasado. Tenga en cuenta que el identificador tendrá el bit de TRANSMIT_HANDLE establecido para los paquetes de transmisión y el bit TRANSMIT_HANDLE borrado para los paquetes de recepción. Para los paquetes de transmisión, esta longitud debe ser el número máximo de bytes que se pueden escribir en el paquete. Para los paquetes de recepción, esta longitud debe ser el número real de bytes del paquete recibido.
Depuración de módulos de extensibilidad de KDNET
Para depurar un módulo de extensibilidad de KDNET, debe ejecutar los siguientes comandos bcdedit desde un símbolo del sistema con privilegios elevados en la máquina de destino.
En primer lugar, y lo más importante, debe ejecutar los dos comandos siguientes para asegurarse de que Winload permitirá errores de arranque repetidos sin pasar por una ruta de errores especial que irrumpa en el depurador e impida el arranque normal. La ejecución de estos comandos le permitirá reiniciar repetidamente la máquina con nuevos bits y depurar esos nuevos bits sin problema.
Bcdedit -set {current} BootStatusPolicy IgnoreAllFailures
Bcdedit -set {current} RecoveryEnabled No
Suponiendo que usará la depuración serie en com1 en la máquina de destino para depurar el módulo de extensibilidad, haga lo siguiente.
bcdedit -dbgsettings serial debugport:1 baudrate:115200
Esto establece el transporte de depuración predeterminado en serie en com1 a 115200 baudios. Esta configuración también se usará para la depuración de arranque.
bcdedit -debug on
Esto habilita la depuración del kernel.
bcdedit -bootdebug on
Esto permite la depuración de arranque en winload.exe, que usará para depurar en la inicialización temprana del kernel, incluido el módulo de extensibilidad de KDNET.
bcdedit -set kerneldebugtype net
Esto obliga al tipo de depuración del kernel a net, independientemente de la configuración de transporte de depuración predeterminada. Esto hará que winload.exe cargue kdnet.dll como transporte de depuración del kernel.
bcdedit -set kernelbusparams b.d.f
Donde b es el número de bus, d es el número de dispositivo y f es el número de función (todo en valores decimales) del hardware para el que está escribiendo el módulo de extensibilidad KDNET. Estos números dependerán de la ranura PCI en la que se encuentre el hardware. Para encontrarlos, busque la cadena de ubicación en la página de propiedades del dispositivo de red en el Administrador de dispositivos Windows. Abra el Administrador de dispositivos de Windows, haga doble clic en los dispositivos de red, busque el dispositivo, haga doble clic en él y, en la ventana que se abre, debe haber un campo Location: que contenga el bus, el dispositivo y la función del hardware en el bus PCI. Si tiene un controlador de bus que hace que esa información se enmascare, tendrá que determinar la ubicación a partir de los controladores, o de alguna otra manera.
Esto fuerza el elemento busparams de kernel a b.d.f, lo que obliga a que ese dispositivo concreto se seleccione como dispositivo de depuración del kernel.
bcdedit -set kernelhostip N
Donde N viene determinado por la fórmula siguiente. Si la máquina del depurador host tiene una dirección IPv4 de w.x.y.z, N = (w0x01000000) + (x0x00010000) + (y0x00000100) + (z0x00000001). N debe especificarse en la línea de comandos en valor decimal, no hexadecimal. De hecho, toma cada byte de la dirección IPv4 y lo concatena (en valor hexadecimal) para crear un número de 32 bits en valor hexadecimal y, a continuación, lo convierte en decimal.
bcdedit -set kernelport N
Donde N es 50000 u otro puerto que no se bloqueará en la red interna.
Esto obliga a KDNET a usar el puerto N como puerto de depuración de red.
bcdedit -set kernelkey 1.2.3.4
Esto fuerza la clave de depuración de KDNET a 1.2.3.4. 1.2.3.4 no es seguro ni único en la clave de red. Para mantener el equipo de destino seguro, los paquetes que viajan entre el equipo host y de destino deben cifrarse. Se recomienda encarecidamente usar una clave de cifrado generada automáticamente. Para obtener más información, consulte Configuración automática de la depuración del kernel de red KDNET.
bcdedit -set kerneldhcp on
Esto fuerza el valor dhcp del kernel KDNET a activado.
Ejecute el depurador en la máquina host del depurador con la siguiente línea de comandos, suponiendo que use com1 como puerto de depuración serie en el equipo host:
windbg -d -k com:port=com1,baud=115200
Esto ejecutará el depurador y hará que se interrumpa cuando el depurador de arranque windbg se comunique primero con el equipo host.
A continuación, reinicie la máquina de destino ejecutando
shutdown -r -t 0
Cuando el depurador irrumpa en windbg, asegúrese de que obtiene símbolos cargados para winload. (es posible que tenga que establecer .sympath y realizar una operación .reload). A continuación, ejecute x winload!*deb*tra*
. Uno de los símbolos enumerados se parecerá a BdDebugTransitions.
A continuación, ejecute ed winload!BdDebugTransitions 1
, pero asegúrese de usar el nombre de símbolo correcto.
A continuación, ejecute bu winload!blbdstop
para establecer un punto de interrupción.
A continuación, pulse g
para acceder.
Debería irrumpir en winload! BlBdStop.
A continuación, ejecute los siguientes comandos.
bu nt!KdInitSystem
bu kdnet!KdInitialize
bu kdstub!KdInitializeLibrary
Tenga en cuenta que lo más probable es que use kdstub al establecer puntos de interrupción en el módulo de extensibilidad de KDNET, si eso no funciona, use
bu kd_YY_XXXX!KdInitializeLibrary
Donde YY es su clase PCI, e XXXX es su VID de PCI. (es decir, use el nombre del módulo de extensibilidad KDNET).
Normalmente, en el depurador deberá usar kdstub en lugar de usar el nombre real del módulo de extensibilidad.
A continuación, ejecute bl
para enumerar los puntos de interrupción. Asegúrese de que los puntos de interrupción estén en su lugar (todos deben tener una e junto a ellos).
A continuación, pulse g
. Debería llegar al punto de interrupción nt!KdInitSystem.
Si vuelve a pulsar g
, debería llegar a kdnet!KdInitialize
Si vuelve a pulsar g
, debería llegar a un punto de interrupción en su propio módulo, en KdInitializeLibrary.
A continuación, puede establecer un punto de interrupción en la rutina InitializeController, así como todas las demás rutinas y recorrer el código.
Una vez que recorra KdInitializeLibrary, pulse g y, si estableció un punto de interrupción en la rutina InitializeController, se llegará a él.
Una vez que finalice, asegúrese de que tiene puntos de interrupción establecidos en KdGetTxPacket, KdSendTxPacket, KdGetRxPacket, KdReleaseRxPacket, vuelva a pulsar g, y esas rutinas se ejecutarán como parte de la inicialización de red realizada por KDNET durante el arranque.
Es posible que tenga que agregar código temporal a las rutinas KdInitializeLibrary o KdInitializeController para asegurarse de que se llame a todas las rutinas para que pueda recorrer todo el código. (Por ejemplo, no se llamará a KdShutdownController cuando todo funcione bien, por lo que deberá realizar la llamada explícitamente desde código temporal para que pueda recorrerlo y asegurarse de que es correcto).
Una vez que haya recorrido todo el código y esté seguro de que es correcto, reinicie el destino, pero no establezca la marca winload!BdDebugTransitions en true (deje el valor predeterminado en cero).
A continuación, ejecute también otra instancia del depurador de kernel en la máquina del depurador host.
Windbg -d -k net:port=50000,key=1.2.3.4
Deje que el equipo de destino arranque, debería conectarse a la otra instancia del depurador de kernel a través de la red.
A continuación, ejecute comandos en el depurador de kernel y asegúrese de que funciona. Deje que el destino continúe el arranque y asegúrese de que puede interrumpir y ejecutar comandos más adelante.
Nota:
Establecer la marca de transiciones de depuración en winload garantiza que Windows no arranque. Si intenta permitir que Windows finalice el arranque después de establecer esa marca, Windows simplemente se bloqueará o fallará. Si desea que Windows arranque correctamente, no puede establecer esa marca de transiciones de depuración. Activar la marca le permite depurar el código, y verificar que es correcto, recorriéndolo en el depurador, pero en última instancia necesitará no activar la marca para poder verificar que la depuración funciona cuando arranca con normalidad. Esto significa que no se puede recorrer el código al arrancar el sistema con normalidad y, de hecho, cuando Windows se ejecuta con normalidad, con la depuración habilitada en el hardware, el módulo de extensibilidad de KDNET no es capaz de depurar. Cualquier intento de depurarlo con el depurador de kernel hará que la máquina se bloquee. (No se pueden establecer puntos de interrupción en el código que se ejecuta en las rutas de depuración del kernel, ya que provoca una reentrada infinita, una pila estropeada y un reinicio).
Varias funciones físicas: 2PF
Además de la extensibilidad de KDNET, KDNET admite la depuración del kernel mediante varias funciones físicas (PF) en las NIC admitidas mediante la partición del espacio de configuración de PCI. Se recomienda a los proveedores de tarjetas de red habilitar la compatibilidad con esta característica. Para obtener más información, consulte Compatibilidad con el controlador de red de minipuerto de depurador 2PF KDNET.
Consulte también
Configuración automática de la depuración del kernel de red KDNET
Configuración manual de la depuración del kernel de red KDNET
Compatibilidad con el controlador de red de minipuerto KDNET 2PF del depurador