Entwicklung von KDNET-Transport-Erweiterungsmodulen
Dieses Thema beschreibt, wie der KDNET-Transport so erweitert werden kann, dass er auf jeder beliebigen Hardware ausgeführt werden kann, indem eine separate Hardwaretreiber-Erweiterungsmodul-DLL verwendet wird. Die Erweiterungsmodule für den KDNET-Transport werden von Anbietern von Netzwerkkarten entwickelt, um den Support für das Debuggen des Kernels für bestimmte Netzwerkkarten hinzuzufügen.
KDNET-Übersicht
KDNET ist ein Transportmittel zum Debuggen des Kernels, das das Debuggen des Windows-Kernels über ein Networking ermöglicht. Ursprünglich wurde es für die Unterstützung von Kernel-Debugging mit Ethernet-NICs verwendet. Es ist so konzipiert, dass die Hardware-Support-Schicht in einem separaten Modul von der Netzwerkpaketverarbeitung und der Kernel-Schnittstellenschicht erstellt wird. Diese Schicht zur Unterstützung des Hardwaretreibers wird als KDNET-Erweiterungsmodul bezeichnet.
KDNET ist der einzige Transport, der durch die Verwendung einer separaten Hardwaretreiber-Erweiterungsmodul-DLL so erweitert werden kann, dass er auf jeder beliebigen Hardware ausgeführt werden kann. Das Ziel ist es, die gesamte Fehlersuche von Windows durch KDNET und KDNET-Erweiterungsmodule zu unterstützen. Alle anderen Kernel-Transport-DLLs (kdcom.dll, kd1394.dll, kdusb.dll usw.) sind veraltet und werden irgendwann aus Windows entfernt.
Es gibt zwei Arten von Schnittstellen, die KDNET für die Kommunikation mit den Erweiterungsmodulen von KDNET verwendet. Die eine ist eine paketbasierte Schnittstelle, die für NICs, USB und drahtlose Hardware verwendet wird, und die andere ist eine bytebasierte Schnittstelle, die für die Unterstützung von KDNET über serielle Hardware verwendet wird.
Die KDNET-Erweiterungsmodule müssen sehr strenge Anforderungen erfüllen, um korrekt zu funktionieren. Da sie zum Debuggen des Kernels verwendet werden, werden sie aufgerufen und ausgeführt, wenn das System die weitere Ausführung von Code aussetzt. Allgemein sind alle Prozessoren im System mit Ausnahme des Prozessors, der mit der auf dem Host-Computer zum Debuggen des Kernels ausgeführten Debugger-Anwendung über den Kernel-Debug-Transport kommuniziert, in einem IPI gesperrt. Dieser Prozessor wird in der Regel mit vollständig deaktivierten Interrupts ausgeführt und arbeitet im Wesentlichen auf der Hardware des Debug-Transports und wartet auf Befehle vom Debugger.
Importieren und Exportieren
Die KDNET-Erweiterungsmodule haben genau einen expliziten Export – KdInitializeLibrary. Sie haben auch keine expliziten Importe. KDNET-Erweiterungsmodulen wird ein Zeiger auf eine Struktur mit einer Liste der Funktionen übergeben, die KDNET beim Aufruf von KdInitializeLibrary zulässt. Es können keine anderen Funktionen aufgerufen werden. Punkt. KDNET-Erweiterungsmodule, die irgendwelche Importe haben, sind falsch konzipiert und werden nicht unterstützt.
Wenn Sie die Importe und Exporte eines KDNET-Erweiterungsmoduls mit link /dump /exports und link /dump /imports ausgeben, werden Sie feststellen, dass es nur einen Export (KdInitializeLibrary) und keine Importe gibt. KDNET-Erweiterungsmodule melden ihre zusätzlichen Exporte an KDNET, indem sie Funktionszeiger in eine Struktur für Exportfunktionen eintragen, an die KDNET einen Zeiger übergibt, wenn KdInitializeLibrary aufgerufen wird. KDNET verwendet dann die Funktionszeiger in dieser Struktur, um das Erweiterungsmodul aufzurufen und Datenübertragungen unter Verwendung der vom Modul unterstützten Hardware durchzuführen. KDNET stellt fest, ob es sich bei dem Modul um ein paketbasiertes oder ein bytebasiertes Modul handelt, indem es sich ansieht, welche spezifischen Funktionen das Modul in der Exportfunktionstabelle in der Struktur füllt. Einige dieser Funktionen dienen der Unterstützung von paketbasierter Hardware, andere sind für serielle Hardware gedacht. Einige der Funktionen in der Tabelle werden sowohl von serieller als auch von paketbasierter Hardware verwendet (KdInitializeController, KdShutdownController, KdGetHardwareContextSize).
Codeentwurf
KDNET-Erweiterungsmodule sollten als Single-Thread-Code geschrieben werden. Sie sollten keine Synchronisation durchführen. Alle Kernel-Debugging-Transporte sind vom Windows-Kernel abhängig, um die richtige Synchronisation durchzuführen, wenn der Debugger aufgerufen wird. Der Kernel verfügt über eine Debugger-Sperre, die er beim Eintritt in den Kernel-Debugger übernimmt, und er sperrt auch die anderen Prozessoren im System in einem IPI, wenn der Debugger aufgerufen wird. Diese Prozessoren werden erst freigegeben, wenn der Kernel-Debugger, der auf dem Host ausgeführt wird, dem Zielcomputer die Möglichkeit bietet, die Ausführung fortzusetzen. Da der Kernel diese Synchronisierung vornimmt, dürfen die KDNET-Erweiterungsmodule in ihrem Code auf keinen Fall Spinlocks, Mutexe, Gates oder andere Windows-Synchronisierungsmechanismen verwenden. Sie sollten so geschrieben werden, dass sie ihre jeweilige Hardware direkt programmieren, um Pakete und/oder Bytes zu senden und zu empfangen.
Der Code des KDNET-Erweiterungsmoduls sollte so einfach wie möglich gehalten werden. Dies wird dazu beitragen, dass er so fehlerfrei wie möglich ist, da das Debuggen von KDNET-Erweiterungsmodul Code live auf einem Computer derzeit ohne den Einsatz eines Hardware-Debuggers nicht möglich ist. Sie können den Kernel-Debugger nicht zum Debuggen des Codes für den Kernel-Debug-Transport verwenden. Der Versuch, dies zu tun, führt zu einem Neustart des Computers aufgrund eines beschädigten Kernel-Stacks (was in der Regel mit einem Doppelfehler und einem Neustart endet) oder zu einem Deadlock oder dazu, dass der Transport erneut gestartet werden muss, was in den meisten Fällen dazu führt, dass der Transport nicht korrekt funktioniert.
Benennungskonventionen für KDNET-Erweiterungsmodule
Ihr Transportmodul zum Debuggen des Kernels muss einer der beiden Namenskonventionen für KDNET-Erweiterungsmodule folgen. Wenn Ihr Modul PCI-basierte Hardware unterstützt, muss es kd_YY_XXXX.dll heißen, wobei XXXX die PCI-Anbieter-ID Ihrer Hardware in Hexadezimalzeichen und YY die PCI-Klasse für Ihre Hardware ist. Es gibt mehrere KDNET-Erweiterungsmodule, die in Windows mitgeliefert werden und PCI-basierte Hardware unterstützen. Zum Beispiel die kd_02_8086.dll von Intel, die kd_02_14e4.dll von Broadcom und die kd_02_10ec.dll von Realtek. Die registrierten PCI-Anbieter-IDs finden Sie unter https://www.pcisig.com/membership/member-companies. Alle PCI-basierten KDNET-Erweiterungsmodule verwenden die Anbieter-VID der von ihnen unterstützten Hardware in Hexadezimalform als die letzten 4 Zeichen im Namen ihres Moduls. Der Code für die meisten In-Box-Module ist 02, da es sich um Geräte der Networking-Klasse handelt und sie daher in ihrem PCI-Konfigurationsbereich die PCI-Klasse 0x02 haben. Winload.exe erstellt den Namen von PCI-basierten KDNET-Erweiterungsmodulen, indem es die PCI-Geräteklasse und die PCI-VID des ausgewählten Debugger-Geräts aus dessen PCI-Konfigurationsbereich liest und versucht, ein Modul mit diesen Kennungen im Namen zu laden. Wenn Ihr Gerät einen PCI-Klassencode hat, der nicht der Netzwerkklasse 0x02 entspricht, müssen Sie im Namen Ihres KDNET-Erweiterungsmoduls den korrekten PCI-Klassencode in hexadezimaler Form für Ihr Gerät verwenden. Andernfalls wird Ihr Modul von Winload nicht korrekt geladen. Die _02_
in jedem dieser Namen ist der PCI-Class-Code für Networking-Class-Geräte in Hexadezimal. Dieser Code wird auch im PCI-Konfigurationsbereich des Debugger-Geräts gefunden und ausgelesen.
Wenn Sie ein Gerät haben, das einen DBG2-Tabelleneintrag hat und kein PCI-basiertes Gerät ist, dann ist die Namenskonvention für Ihr Modul anders. Die Namenskonvention für DBG2-Tabellen-Debug-Geräte lautet kd_XXXX_YYYY.dll, wobei XXXX der hexadezimale DBG2-Tabellen-PortType und YYYY der hexadezimale DBG2-Tabellen-PortSubtype aus dem DBG2-Tabelleneintrag ist. Kd_8003_5143.dll ist eine Inbox-DLL für die Unterstützung eines net (0x8003) PortType mit einem Subtyp 0x5143. In diesem Fall ist 5143 die Qualcomm-PCI-VID, da dies für die Unterstützung von KDNET auf Qualcomm-USB-Controllern gedacht ist, und für Net-DBG2-Tabelleneinträge ist der PortSubtype als PCI-VID für den Anbieter der Hardware definiert. Beachten Sie, dass Sie serielle, USB- und andere DBG2-Tabellengeräte mit dieser Namenskonvention unterstützen können. Im Folgenden finden Sie die derzeit unterstützten PortType-Werte in Hexadezimal: 8000 für serielle Geräte, 8001 für 1394-Geräte, 8002 für USB-Geräte, 8003 für Net-Geräte. Beachten Sie, dass die Subtypen für serielle und USB-Geräte bei Microsoft reserviert werden müssen. Microsoft unterhält eine Liste der zugewiesenen seriellen und USB-Subtypen. Bitte senden Sie eine E-Mail an kdnet@microsoft.com, um einen seriellen oder USB-Subtyp zu reservieren, wenn die vorhandenen unterstützten Typen nicht mit Ihrer Hardware funktionieren.
KDNET-Erweiterungsimporte
Im Folgenden finden Sie eine Liste der Funktionen, die Sie von einem KDNET-Erweiterungsmodul aus aufrufen können. Beachten Sie, dass alle diese Routinen an die Routine KdInitializeLibrary übergeben werden und dass der Header kdnetextensibility.h normale Aufrufe dieser Routinen so umleitet, dass sie über die Importtabelle erfolgen. Ihr Code muss diese über die Importtabelle aufrufen, damit Ihr Modul keine Importe enthält. Sie dürfen keine anderen Funktionen aufrufen, die vom Kernel, der HAL oder einem anderen Kernel-Modul exportiert werden. Sie dürfen nur diese Funktionen aufrufen. Diese festgelegten Funktionen haben sich als ausreichend erwiesen, um alle KDNET-Erweiterungsmodule zu entwickeln, und sollten für normale Szenarien ausreichen. Wenn Sie zusätzliche Routinen benötigen, die vom Kernel exportiert werden, aber nicht in dieser Liste enthalten sind, senden Sie bitte eine E-Mail an kdnet@microsoft.com, in der Sie Ihr Szenario erläutern und angeben, welche zusätzlichen Funktionen Sie benötigen und warum. Beachten Sie, dass diese Liste, wenn überhaupt, nur bei größeren Windows-Release-Zyklen ergänzt wird. Beachten Sie, dass die meisten dieser Funktionen direkt den Windows-Kernel-APIs entsprechen, die entweder vom Kernel oder der HAL unterstützt werden. Eine oder zwei sind angepasste Funktionen, die nur für KDNET gelten.
Es ist wichtig, dass Sie die Datei kdnetextensibility.h ordnungsgemäß in Ihre Header einbinden, damit die korrekte Neuzuordnung der Funktionen durch die Importtabelle erfolgen kann. Wenn Sie dies nicht tun, wird Ihr Modul Importe haben und nicht unterstützt werden.
Lesezugriff auf Arbeitsspeicherroutinen
Die folgenden Funktionen sollten für das Lesen und Schreiben von im Speicher abgebildeten Geräten verwendet werden. Diese haben die gleiche Aufrufkonvention und sind den entsprechenden Funktionen des Kernels zugeordnet: READ_REGISTER_UCHAR, READ_REGISTER_USHORT, READ_REGISTER_ULONG, WRITE_REGISTER_UCHAR, WRITE_REGISTER_USHORT, WRITE_REGISTER_ULONG und auf 64-Bit-Plattformen nur READ_REGISTER_ULONG64 und WRITE_REGISTER_ULONG64. Alle Zugriffe auf den Speicher des Geräts sollten über diese Funktionen erfolgen, da sie sicherstellen, dass Lese- und Schreibvorgänge nicht vom Prozessor neu geordnet werden. Beachten Sie, dass auf msdn.microsoft.com Windows CE Compact 2013-Routinen dokumentiert sind, die in der Aufrufkonvention mit diesen Funktionen übereinstimmen. Leider scheinen die NT-Routinen nicht dokumentiert zu sein, aber die Aufrufkonvention ist die gleiche.
Lesen von E/A-Portroutinen
Die folgenden Funktionen sollten für das Lesen und Schreiben auf Geräte-IO-Ports verwendet werden. Diese haben die gleiche Aufrufkonvention und sind den entsprechenden Funktionen des Kernels zugeordnet: READ_PORT_UCHAR, READ_PORT_USHORT, READ_PORT_ULONG, WRITE_PORT_UCHAR, WRITE_PORT_USHORT und WRITE_PORT_ULONG. Alle Zugriffe auf den IO-Port des Geräts sollten über diese Funktionen erfolgen. Beachten Sie, dass auf msdn.microsoft.com Windows CE Compact 2013-Routinen dokumentiert sind, die in der Aufrufkonvention mit diesen Funktionen übereinstimmen.
Zusätzliche Routinen
Die folgenden zusätzlichen Funktionen können aufgerufen werden und sollten normal mit den angegebenen Parametern aufgerufen werden. Beachten Sie, dass Sie dabei zwar den Header kdnetextensibility.h ordnungsgemäß einbinden, aber die Funktionsaufrufe über die KDNET-Erweiterungstabelle neu zuordnen, sodass keine expliziten Importe in Ihrem Modul erforderlich sind, wie es bei KDNET-Erweiterungsmodulen der Fall ist.
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
);
Beachten Sie, dass die Funktion PoSetHiberRange nur von der Funktion KdSetHibernateRange aufgerufen werden sollte. Außerdem sollten die meisten KDNET-Erweiterungsmodule die Funktionen KeBugCheckEx, KdMapPhysicalMemory64 und KdUnmapVirtualAddress nicht aufrufen müssen. Andererseits werden im Wesentlichen alle KDNET-Erweiterungsmodule KdGetPhysicalAddress aufrufen müssen, um physische Speicheradressen zu erhalten, die zur Programmierung von Geräte-DMA-Engines erforderlich sind, und viele werden KeStallExecutionProcessor, KdGetPciDataByOffset und KdSetPciDataByOffset aufrufen müssen. Die letzten beiden Funktionen dienen dem Zugriff auf den PCI-Konfigurationsbereich des Geräts.
KDNET-Erweiterungsexporte
Im Folgenden finden Sie eine kurze Beschreibung der einzelnen Funktionen der KDNET-Erweiterung. Sie müssen alle Funktionen implementieren, die entweder für ein paketbasiertes KDNET-Erweiterungsmodul oder für ein serielles KDNET-Erweiterungsmodul erforderlich sind. Im Folgenden finden Sie die Exporte des paketbasierten KDNET-Erweiterungsmoduls.
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;
}
Diese Routine wird aufgerufen, um die Import- und Exportroutinen zwischen KDNET und diesem KDNET-Erweiterungsmodul zu übergeben. Diese Funktion sollte überprüfen, ob die Version der Import- und Exporttabellen erwartet und unterstützt wird, und andernfalls fehlschlagen. Sie sollte eine Kopie der Importtabelle in ihrem eigenen globalen Speicher erstellen. Sie sollte die Funktionen, die sie exportiert, in die Struktur schreiben, auf die das Feld Exports der Importtabelle zeigt. Außerdem muss sie das Feld Length der Struktur Memory, die Teil des an diese Routine übergebenen Zeigers auf den Debugger-Deskriptor ist, auf die Menge an Speicher-Bytes festlegen, die sie für die Unterstützung des Hardware-Geräts benötigt.
Überprüfen der Importexportanzahl
Der Code muss überprüfen, ob Imports FunctionCount dem entspricht, was im Betriebssystem verfügbar ist, z. B. und (KdNetExtensibilityImports->FunctionCount != KDNET_EXT_IMPORTS)
gibt zurück STATUS_INVALID_PARAMETER
, wenn die Anzahl nicht übereinstimmt.
Durch die Überprüfung der Anzahl wird die Kompatibilität zwischen der ausgeführten Windows OS KDNET-Version (z. B. Boot-Manager/OS Loader/Hypervisor/Secure kernel/NT OS, alle Verknüpfungen zur KDNET-Bibliothek) und der WDK-Version sichergestellt, die zum Erstellen des aktuellen KDNET-Erweiterungsmoduls verwendet wird. Die WDK- und Betriebssystemversion müssen synchronisiert werden; andernfalls schlägt die obige Überprüfung fehl, wenn sich der Wert des Import-/Exportindikators ändert. Wenn beispielsweise die KDNET-Erweiterbarkeitsschnittstelle eine neue Featurefunktion hinzugefügt hat, entspricht die Anzahl nicht mehr. Um sicherzustellen, dass sie übereinstimmen, verwenden Sie immer die WDK-Version, die dem Betriebssystem entspricht, das die KDNET-Version hosten.
Anpassen des erforderlichen Arbeitsspeichers
Beachten Sie, dass das Gerät mit der für den Debugger ausgewählten Hardware gefüllt wird. Diese Funktion sollte die benötigte Speichermenge an das Gerät anpassen, falls erforderlich. Zum Beispiel können Erweiterungsmodule, die sowohl 1Gig- als auch 10Gig-Hardware unterstützen, die angeforderte Speichergröße für 10Gig-Geräte erhöhen. Sie können feststellen, welches Gerät verwendet wird, indem Sie das Feld DeviceID des Debug-Geräte-Deskriptors untersuchen.
KdInitializeLibrary ist der einzige Export
Beachten Sie, dass diese Funktion sowohl von Winload als auch von KDNET während des KdInitSystem-Aufrufs aufgerufen wird. Beachten Sie, dass dies die EINZIGE Funktion ist, die von KDNET-Erweiterungsmodulen exportiert wird. Sie ist die einzige Funktion, die in einer DEF-Datei enthalten ist. KDNET-Erweiterungsmodule haben genau eine explizite Exportfunktion – diese Routine – und keine Importfunktion.
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.
--*/
Diese Routine wird vom System vor dem Ruhezustand aufgerufen, damit es den vom KDNET-Erweiterungsmodul verwendeten Code ordnungsgemäß beim System registrieren kann. Dies bietet dem System die Möglichkeit, den Speicher während des Ruhezustands ordnungsgemäß zu verwalten und aus dem Ruhezustand wieder aufzunehmen. (Der Speicher wird spät gespeichert und früh geladen, da er bei der Wiederaufnahme sehr früh aufgerufen wird).
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.
--*/
Diese Funktion wird zur Initialisierung der Hardware aufgerufen. Sie wird aufgerufen, wenn das System initialisiert wird und immer dann, wenn das System aus einem Energiesparzustand erwacht, für den es KdShutdownController aufgerufen hat. Diese Funktion MUSS sicherstellen, dass die Hardware die Initialisierung vollständig abgeschlossen hat und dass sie bereit ist, Pakete zu senden, BEVOR sie zurückkehrt. Diese Funktion sollte darauf warten, dass der PHY hochfährt und der Link hergestellt wird. Beachten Sie, dass diese Funktion nicht unendlich lange warten sollte, wenn kein Kabel angeschlossen ist. Diese Funktion legt die Link-Geschwindigkeit und den Duplex in der gemeinsamen Datenstruktur von KDNET und diesem Erweiterungsmodul fest. Außerdem schreibt sie die von der Hardware verwendete MAC-Adresse an die Stelle, auf die TargetMacAddress in der gemeinsamen Datenstruktur von KDNET zeigt.
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 ist von entscheidender Bedeutung, dass diese Funktion WARTET, bis alle noch ausstehenden Übertragungspakete tatsächlich über die Leitung gesendet wurden. Diese Funktion muss warten, bis alle Übertragungspakete aus dem Hauptspeicher per DMA übertragen wurden und über die Leitung gesendet wurden, BEVOR sie die Übertragung in der Hardware beendet. Sobald alle anstehenden Übertragungspakete gesendet wurden, sollte diese Funktion die Hardware vollständig abschalten. Diese Funktion wird aufgerufen, wenn das System heruntergefahren wird und auch, wenn das System beschließt, den Debug-Transport in einen Energiesparzustand zu versetzen. Diese Routine kann aufgerufen werden, wenn das System in den Standby-Modus, in den Ruhezustand, in den Ruhezustand und in den Connected-Standby-Modus wechselt, zusätzlich zum Herunterfahren des Systems.
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.
--*/
Diese Funktion sollte die Anzahl der Bytes zurückgeben, die für den gesamten Speicher benötigt werden, der für die Unterstützung Ihrer Hardware erforderlich ist. Dazu gehören Ihre Context-Struktur und alle Paketpuffer für Empfang und Übertragung sowie alle Hardware-Paketdeskriptoren und andere Strukturen. Die Größe des gesamten von Ihnen benötigten Speichers muss hier angegeben werden. Einschließlich des zusätzlichen Speichers, der für Ausrichtungsbeschränkungen Ihrer Hardware für Pakete, Paketdeskriptoren oder andere Strukturen erforderlich ist.
Beachten Sie, dass diese Funktion von Ihrer KdInitializeLibrary-Routine aufgerufen werden sollte, wenn diese das Feld MemoryLength im Debug-Deskriptor des Geräts festlegt.
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.
--*/
Diese Funktion ruft das nächste verfügbare Paket ab, das empfangen, aber noch nicht verarbeitet wurde. Sie gibt ein Handle für dieses Paket zurück. Das Handle wird verwendet, um die Adresse des Pakets durch den Aufruf von KdGetPacketAddress und die Länge durch den Aufruf von KdGetPacketLength zu erhalten. Das Paket und das Handle müssen verfügbar und gültig bleiben, bis das Paket durch den Aufruf von KdReleaseRxPacket freigegeben wird. Diese Funktion gibt auch direkt die Adresse und Länge des Pakets an den Aufrufer zurück.
Wenn derzeit kein Paket verfügbar ist, MUSS diese Funktion sofort mit STATUS_IO_TIMEOUT zurückkehren. Diese Funktion MUSS NICHT auf den Empfang eines Pakets warten. Beachten Sie, dass die höchsten 2 Bits von Handle reserviert sind. TRANSMIT_HANDLE und TRANSMIT_ASYNC müssen beide leer sein.
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.
--*/
Diese Funktion gibt die Ressourcen, die mit dem Paket-Handle verbunden sind, an die Hardware zurück, sodass sie für den Empfang eines anderen Pakets verwendet werden können. Auf jeden erfolgreichen Aufruf von KdGetRxPacket folgt ein weiterer Aufruf von KdReleaseRxPacket mit dem von KdGetRxPacket zurückgegebenen Handle. Beachten Sie, dass es NICHT garantiert ist, dass KdReleaseRxPacket unmittelbar nach dem Erfolg von KdGetRxPacket aufgerufen wird. Es ist möglich, dass zuerst ein anderer KdGetRxPacket-Aufruf erfolgt. Bei jedem erfolgreichen KdGetRxPacket-Aufruf werden jedoch die Ressourcen mit einem KdReleaseRxPacket-Aufruf freigegeben.
Diese Funktion sollte die Hardware ordnungsgemäß programmieren, damit die freigegebenen Ressourcen für den Empfang eines weiteren Pakets verwendet werden können.
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.
--*/
Diese Funktion holt sich die nächsten verfügbaren Übertragungsressourcen und gibt einen Handle darauf zurück. Dieses Handle wird für den Aufruf von KdGetPacketAddress und KdGetPacketLength verwendet. Die Paketadresse, die von KdGetPacketAddress zurückgegeben wird, wird verwendet, um den Inhalt des Pakets direkt zu schreiben. Die Paketadresse muss der Anfang des Pakets sein, und die Länge sollte die maximale Anzahl von Bytes sein, die in das Paket geschrieben werden können. Beachten Sie, dass diese Funktion sofort STATUS_IO_TIMEOUT zurückgeben sollte, wenn keine Hardware-Ressourcen mehr verfügbar sind, weil sie alle bereits angefordert wurden und noch nicht übertragen worden sind.
TRANSMIT_HANDLE muss in dem zurückgegebenen Handle festgelegt werden. Beachten Sie, dass die höchsten zwei Bits von Handle für die Flags TRANSMIT_ASYNC und TRANSMIT_HANDLE reserviert sind.
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.
--*/
Diese Funktion sendet das Paket, das mit dem übergebenen Handle verknüpft ist, über die Leitung. Beachten Sie, dass in Handle ein zusätzliches Bit festgelegt sein kann, das anzeigt, ob es sich um eine asynchrone Übertragung handelt oder nicht. Wenn das Flag TRANSMIT_ASYNC im Handle festgelegt ist, sollte diese Funktion die Hardware so programmieren, dass sie das Paket sendet, und dann sofort zurückkehren, ohne darauf zu warten, dass die Hardware den Sendevorgang abschließt. Das bedeutet, dass alle Fehler, die während der Übertragung auftreten, verloren gehen. Das ist in Ordnung und by-design, da Pakete auf dem Übertragungsweg ohnehin verloren gehen können. Wenn das Flag TRANSMIT_ASYNC im Handle nicht festgelegt ist, MUSS diese Funktion warten, bis das Paket über die Leitung gesendet wurde, und sollte jeden Fehler, der während der Übertragung auftritt, zurückgeben. Beachten Sie, dass TRANSMIT_ASYNC festgelegt wird, wenn Dump-Dateien an den Host des Debuggers gesendet werden oder wenn Windows-Networking-Pakete von KDNIC über KDNET gesendet werden. Wenn alle anderen Debugger-Pakete gesendet werden, ist TRANSMIT_ASYNC deaktiviert.
Wenn eine Reihe von Paketen mit TRANSMIT_ASYNC WAHR gesendet wird, gefolgt von einem Paket, bei dem TRANSMIT_ASYNC nicht festgelegt ist, muss die Hardware warten, bis das Paket ohne das festgelegte Flag tatsächlich gesendet wird, auch wenn dies bedeutet, dass sie warten muss, bis die vorherigen asynchronen Pakete ebenfalls gesendet werden.
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.
--*/
Diese Funktion gibt einen Zeiger auf das erste Byte des Pakets zurück, das mit dem übergebenen Handle verknüpft ist. Beachten Sie, dass das Handle das TRANSMIT_HANDLE-Bit für Übertragungspakete festgelegt hat und das TRANSMIT_HANDLE-Bit für Empfangspakete gelöscht ist. Der zurückgegebene Zeiger sollte eine virtuelle Windows-Adresse sein, die vom Prozessor gelesen oder geschrieben werden kann. Diese Adresse sollte innerhalb des Speicherblocks liegen, der für das KDNET-Erweiterungsmodul reserviert ist, das in der Debug-Gerätedeskriptor-Speicherstruktur übergeben wird. (Beachten Sie, dass das KDNET-Erweiterungsmodul beim Zugriff auf diesen Speicher NIEMALS mehr als die von ihm in KdInitializeLibrary angeforderte Speichergröße verwenden sollte. Jeder zusätzliche Speicher am Ende des Blocks ist für die Verwendung durch KDNET reserviert und darf vom KDNET-Erweiterungsmodul nicht angetastet werden.)
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.
--*/
Diese Funktion gibt die Länge des Pakets, das mit dem übergebenen Handle verknüpft ist, in Bytes zurück. Beachten Sie, dass das Handle das TRANSMIT_HANDLE-Bit für Übertragungspakete festgelegt hat und das TRANSMIT_HANDLE-Bit für Empfangspakete gelöscht ist. Bei Übertragungspaketen sollte diese Länge die maximale Anzahl von Bytes sein, die in das Paket geschrieben werden können. Für empfangene Pakete sollte diese Länge die tatsächliche Anzahl der Bytes im empfangenen Paket sein.
Fehlersuche bei KDNET-Erweiterungsmodulen
Um ein KDNET-Erweiterungsmodul zu debuggen, müssen Sie die folgenden bcdedit-Befehle von einer Eingabeaufforderung mit erhöhten Zugriffsrechten auf dem Zielcomputer ausführen.
Zunächst und vor allem müssen Sie die folgenden beiden Befehle ausführen, um sicherzustellen, dass Winload wiederholte Boot-Fehler zulässt, ohne einen speziellen Fehlerpfad zu verwenden, der den Debugger unterbricht und einen normalen Start verhindert. Das Ausführen dieser Befehle bietet Ihnen die Möglichkeit, den Computer wiederholt mit neuen Bits neu zu starten und diese neuen Bits ohne Probleme zu debuggen.
Bcdedit -set {current} BootStatusPolicy IgnoreAllFailures
Bcdedit -set {current} RecoveryEnabled No
Angenommen, Sie verwenden serielles Debugging auf com1 auf dem Zielcomputer, um das Erweiterungsmodul zu debuggen, gehen Sie wie folgt vor.
bcdedit -dbgsettings serial debugport:1 baudrate:115200
Damit legen Sie den Standard-Debug-Transport auf seriell auf com1 mit 115200 Baud fest. Diese Einstellungen werden auch für das Boot-Debugging verwendet.
bcdedit -debug on
Dies ermöglicht das Debuggen des Kernels.
bcdedit -bootdebug on
Dies aktiviert das Debuggen des Kernels in winload.exe, das Sie zum Debuggen der frühen Kernel-Initialisierung einschließlich Ihres KDNET-Erweiterungsmoduls verwenden werden.
bcdedit -set kerneldebugtype net
Dies zwingt den Debuggen-Typ des Kernels auf Net, unabhängig von den Standardeinstellungen für den Debug-Transport. Dadurch wird winload.exe veranlasst, kdnet.dll als Kernel-Debug-Transport zu laden.
bcdedit -set kernelbusparams b.d.f
Dabei steht b für die Busnummer, d für die Gerätenummer und f für die Funktionsnummer – alle in Dezimalzahlen – der Hardware, für die Sie das KDNET-Erweiterungsmodul schreiben. Diese Zahlen hängen davon ab, in welchem PCI-Slot sich die Hardware befindet. Sie können diese finden, indem Sie die Zeichenfolge in den Geräteeigenschaften des Netzwerkgeräts im Windows-Gerätemanager suchen. Öffnen Sie den Windows-Gerätemanager, doppelklicken Sie auf Netzwerkgeräte, suchen Sie Ihr Gerät, doppelklicken Sie darauf und in dem sich öffnenden Fenster sollte ein Feld Position: vorhanden sein, das den Bus, das Gerät und die Funktion der Hardware auf dem PCI-Bus enthält. Wenn Sie einen Bustreiber haben, der diese Informationen ausblendet, müssen Sie die Position über Ihre Treiber oder auf andere Weise ermitteln.
Dies setzt die Kernel-Busparams auf b.d.f – , was dazu führt, dass dieses bestimmte Gerät als Kernel-Debug-Gerät ausgewählt wird.
bcdedit -set kernelhostip N
Dabei wird N durch die folgende Formel bestimmt. Wenn Ihr Host-Debugger-Computer eine IPv4 Adresse w.x.y.z hat, dann ist N = (w0x01000000) + (x0x00010000) + (y0x00000100) + (z0x00000001). N muss in der Kommandozeile in dezimaler Form angegeben werden, nicht in Hex. Im Grunde genommen nehmen Sie jedes Byte der IPv4-Adresse und verketten es (in Hexadezimal), um eine 32-Bit-Zahl in Hexadezimal zu bilden, die Sie dann in Dezimal umwandeln.
bcdedit -set kernelport N
Dabei ist N gleich 50000 oder ein anderer Port, der in Ihrem internen Netzwerk nicht blockiert wird.
Dadurch wird KDNET veranlasst, Port N als Netzwerk-Debug-Port zu verwenden.
bcdedit -set kernelkey 1.2.3.4
Dadurch wird der Debug-Schlüssel von KDNET auf 1.2.3.4 festgelegt. 1.2.3.4 ist kein sicherer oder eindeutiger Schlüssel für das Netzwerk. Um den Zielcomputer sicher zu halten, müssen Pakete, die zwischen dem Host und den Zielcomputern reisen, verschlüsselt werden. Wir empfehlen Ihnen dringend, einen automatisch generierten Verschlüsselungsschlüssel zu verwenden. Weitere Informationen finden Sie unter KDNET-Network-Kernel-Debugging automatisch einrichten.
bcdedit -set kerneldhcp on
Dadurch wird die dhcp-Einstellung des KDNET-Kernels auf Ein festgelegt.
Führen Sie den Debugger auf dem Host-Computer mit der folgenden Kommandozeile aus, vorausgesetzt, Sie verwenden com1 als seriellen Debug-Port auf dem Host-Computer:
windbg -d -k com:port=com1,baud=115200
Damit führen Sie den Debugger aus und veranlassen ihn, sich einzuschalten, wenn der windbg-Boot-Debugger zum ersten Mal mit dem Host-Computer kommuniziert.
Starten Sie dann den Zielcomputer neu, indem Sie Folgendes ausführen
shutdown -r -t 0
Wenn der Debugger in windbg einsteigt, stellen Sie sicher, dass die Symbole für winload geladen werden. (Eventuell müssen Sie den .sympath festlegen und einen .reload durchführen). Führen Sie dann x winload!*deb*tra*
aus. Eines der aufgelisteten Symbole wird etwas wie BdDebugTransitions sein.
Führen Sie dann ed winload!BdDebugTransitions 1
aus, aber stellen Sie sicher, dass Sie den richtigen Symbolnamen verwenden.
Führen Sie dann bu winload!blbdstop
aus, um einen Haltepunkt festzulegen.
Verwenden Sie dann g
, um zu beginnen.
Sie sollten den Haltepunkt bei winload!BlBdStop erreichen.
Führen Sie anschließend die folgenden Befehle aus.
bu nt!KdInitSystem
bu kdnet!KdInitialize
bu kdstub!KdInitializeLibrary
Beachten Sie, dass Sie höchstwahrscheinlich kdstub verwenden werden, wenn Sie Haltepunkte in Ihrem KDNET-Erweiterungsmodul festlegen, falls das nicht funktioniert, verwenden Sie
bu kd_YY_XXXX!KdInitializeLibrary
Dabei steht YY für Ihre PCI-Klasse und XXXX für Ihre PCI-VID. (Beispiel: Verwenden Sie den Namen Ihres KDNET-Erweiterungsmoduls.)
In der Regel müssen Sie im Debugger kdstub verwenden, anstatt den tatsächlichen Namen Ihres Erweiterungsmoduls zu verwenden.
Führen Sie dann bl
aus, um die Haltepunkte aufzulisten. Vergewissern Sie sich, dass die Haltepunkte In-Place sind (sie sollten alle ein e neben sich haben).
Drücken Sie dann g
. Sie sollten auf den Haltepunkt nt!KdInitSystem stoßen.
Drücken Sie erneut g
, und Sie sollten auf kdnet!KdInitialize treffen.
Drücken Sie erneut g
, und Sie sollten einen Haltepunkt in Ihrem eigenen Modul bei KdInitializeLibrary finden.
Dann können Sie einen Haltepunkt in Ihrer InitializeController-Routine sowie in allen anderen Routinen festlegen und Ihren Code Schritt für Schritt durchgehen.
Sobald Sie KdInitializeLibrary durchlaufen haben, drücken Sie G. Wenn Sie einen Haltepunkt für Ihre InitializeController-Routine festgelegt haben, wird diese als Nächstes ausgelöst.
Sobald dies abgeschlossen ist, vergewissern Sie sich, dass Sie Haltepunkte für die Funktionen KdGetTxPacket, KdSendTxPacket, KdGetRxPacket und KdReleaseRxPacket festgelegt haben, und drücken Sie erneut G. Diese Funktionen werden als Teil der Netzwerkinitialisierung ausgeführt, die KDNET während des Bootens durchführt.
Möglicherweise müssen Sie temporären Code in Ihre KdInitializeLibrary- oder KdInitializeController-Routinen einfügen, um sicherzustellen, dass alle Ihre Routinen aufgerufen werden, damit Sie Ihren gesamten Code durchgehen können. (KdShutdownController zum Beispiel wird während des Starts nicht aufgerufen, wenn alles normal funktioniert. Sie müssen ihn also explizit von temporärem Code aus aufrufen, damit Sie ihn Schritt für Schritt durchgehen und sicherstellen können, dass er korrekt ist.)
Wenn Sie Ihren gesamten Code durchgegangen sind und sicher sind, dass er korrekt ist, starten Sie das Ziel neu, aber legen Sie das Flag winload!BdDebugTransitions NICHT auf Wahr fest (lassen Sie es standardmäßig auf Null).
Führen Sie dann auch eine weitere Instanz des Kernel-Debuggers auf Ihrem Host-Debugger Computer aus.
Windbg -d -k net:port=50000,key=1.2.3.4
Lassen Sie den Ziel-Computer booten, und er sollte sich über das Netzwerk mit der anderen Instanz des Kernel-Debuggers verbinden.
Führen Sie dann Befehle im Kernel-Debugger aus, und vergewissern Sie sich, dass er funktioniert. Lassen Sie dann den Zielrechner weiter booten, und vergewissern Sie sich, dass Sie später wieder einsteigen und Befehle ausführen können.
Hinweis
Das Festlegen des Flag für Debug-Übergänge in Winload garantiert, dass Windows NICHT booten wird. Wenn Sie versuchen, Windows nach dem Festlegen dieses Flags die Möglichkeit zu geben, den Bootvorgang abzuschließen, wird Windows einfach abstürzen oder sich aufhängen. Wenn Sie wollen, dass Windows erfolgreich bootet, dürfen Sie dieses Flag für die Debugging-Übergänge nicht festlegen. Das Festlegen des Flags bietet Ihnen die Möglichkeit, Ihren Code zu debuggen und zu überprüfen, ob er korrekt ist, indem Sie ihn im Debugger durchgehen, aber letztendlich müssen Sie das Flag nicht festlegen, damit Sie überprüfen können, ob das Debugging beim normalen Booten funktioniert. Das bedeutet, dass Sie Ihren Code nicht durchgehen können, wenn das System normal gebootet wird. Wenn Windows normal ausgeführt wird und das Debugging auf Ihrer Hardware aktiviert ist, ist Ihr KDNET-Erweiterungsmodul nicht debuggingfähig. Jeder Versuch, es mit dem Kernel-Debugger zu debuggen, führt zum Absturz des Computers. (Sie können in Code, der in den Kernel-Debug-Pfaden ausgeführt wird, keine Haltepunkte festlegen, da dies zu einem unendlichen Wiedereintritt, einem aufgeblähten Stack und einem Neustart führt.)
Mehrere physische Funktionen – 2PF
Zusätzlich zur KDNET-Erweiterung unterstützt KDNET das Debuggen des Kernels mit mehreren Physical Functions (PFs) auf den unterstützten NICs durch Partitioning des PCI-Konfigurationsraums. Netzwerkkartenanbieter werden aufgefordert, die Unterstützung für diese Funktion zu aktivieren. Weitere Informationen finden Sie unter Debugger 2PF KDNET Miniport Network Driver Support.
Siehe auch
Automatisches Einrichten des KDNET-Netzwerkkernel-Debuggings
Manuelles Debuggen des KDNET-Netzwerkkerns einrichten
Debugger-Unterstützung für 2PF-KDNET-Miniport-Netzwerktreiber