開發 KDNET 傳輸擴充性模組
本主題描述如何使用個別的硬體驅動程序擴充性模組 dll,擴充 KDNET 傳輸,以在任何硬體上執行。 KDNET 傳輸擴充性模組是由網路卡廠商所開發,可為特定網路卡新增核心偵錯支援。
KDNET 概觀
KDNET 是一種核心偵錯傳輸,可讓您透過網路對視窗進行核心偵錯。 一開始,它用來支援使用乙太網路 NIC 進行核心偵錯。 其設計目的是讓硬體支援層內建在與網路封包處理和核心介面層不同的模組中。 此硬體驅動程式支援層稱為 KDNET 擴充性模組。
KDNET 是唯一可透過使用個別硬體驅動程式擴充性模組 dll 在任何硬體上執行的傳輸。 它是透過 KDNET 和 KDNET 擴充性模組支援所有 Windows 偵錯的目標。 所有其他核心傳輸 DLL(kdcom.dll、kd1394.dll、kdusb.dll等)最終都會從 Windows 淘汰和移除。
KDNET 使用兩種類型的介面來與 KDNET 擴充性模組通訊。 其中一個是用於 NIC、USB 和無線硬體的封包型介面,另一個是位元組型介面,用來透過序列硬體支援 KDNET。
KDNET 擴充性模組必須遵循非常嚴格的需求,才能正確運作。 因為它們用於核心偵錯,因此會在系統阻礙進一步執行程式代碼時呼叫和執行它們。 一般而言,除了透過核心偵錯傳輸與在主計算機上執行的調試程式應用程式通訊的處理器外,系統中的所有處理器都會鎖定在 PI 中旋轉。 該處理器通常會在中斷完全停用時執行,而且基本上會在偵錯傳輸硬體上旋轉,等待命令來自調試程式。
匯入和匯出
KDNET 擴充性模組只有一個明確導出 – KdInitializeLibrary。 它們也沒有明確的匯入。 KDNET 擴充性模組會傳遞結構指標,其中包含 KDNET 呼叫 KdInitializeLibrary 時允許其呼叫的例程清單。 無法呼叫其他例程。 期間。 任何匯入的 KDNET 擴充性模組設計不正確,且不受支援。
如果您使用連結 /dump /export 和 link /dump /imports 傾印 KDNET 擴充模組的匯入和匯出,您會看到它們只有一個匯出 (KdInitializeLibrary),而且沒有匯入。 KDNET 擴充性模組會藉由在呼叫 KdInitializeLibrary 時,在 KDNET 傳遞指標的導出函式結構中填入函式指標,以報告其額外導出至 KDNET。 KDNET 接著會使用該結構中的函式指標來呼叫擴充性模組,並使用模組支援的硬體來影響數據傳輸。 KDNET 會藉由查看模組在 結構中填入匯出函式數據表中的哪個特定函式,判斷模組是否為封包型或位元組型模組。 其中有些功能用於支援封包型硬體,有些則用於序列型硬體。 數據表中的部分函式會由序列和封包型硬體使用(KdInitializeController、KdShutdownController、KdGetHardwareContextSize)。
程式代碼設計
KDNET 擴充性模組應撰寫為單個線程程序代碼。 它們不應該執行任何同步處理。 所有核心偵錯傳輸都取決於 Windows 核心,以在調試程式輸入時執行適當的同步處理。 核心具有調試程式鎖定,其進入核心調試程式時會採用,而且當調試程式進入調試程式時,它也會鎖定系統中的其他處理器。 只有在主機上執行的核心調試程式告訴目標機器允許繼續執行時,才會釋放這些處理器。 因為核心會執行這項同步處理,KDNET 擴充性模組絕對不得在其程式代碼中使用任何微調鎖定、Mutex、閘道或任何其他 Windows 同步處理機制。 它們應該寫入直接程序設計其各自的硬體,以傳送和接收封包和或位元組。
KDNET 擴充性模組程式代碼應該盡可能簡單。 這有助於確保它盡可能無 Bug,因為目前無法在不使用硬體調試程式的情況下,在機器上偵錯 KDNET 擴充性模組程式代碼。 您無法使用核心除錯程式來偵錯核心偵錯傳輸程式代碼。 嘗試這樣做會導致計算機因為一個吹入的核心堆疊而重新啟動(通常以雙重錯誤和重新啟動結束),或死結,或會導致重新進入傳輸,這在大部分情況下會導致傳輸無法正常運作。
KDNET 擴充性模組命名慣例
您的核心偵錯傳輸模組必須遵循 KDNET 擴充性模組的兩個命名慣例之一。 如果您的模組支援PCI型硬體,則必須將它命名為 kd_YY_XXXX.dll其中XXXX是十六進位中硬體的PCI廠商標識碼,而YY是硬體的PCI類別。 有數個 KDNET 擴充性模組隨附於支援 PCI 型硬體的 Windows 方塊中。 例如,Intel 的kd_02_8086.dll、Broadcom 的kd_02_14e4.dll和 Realtek 的kd_02_10ec.dll。 您可以在所有 PCI 型 KDNET 擴充性模組中查閱已註冊的 PCI 廠商識別碼 https://www.pcisig.com/membership/member-companies ,以十六進位方式支援的硬體廠商 VID 作為模組名稱的最後 4 個字元。 方塊模組中大部分的類別代碼都是 02,因為它們是網路類別裝置,因此在其 PCI 設定空間中具有0x02的 PCI 類別。 Winload.exe從其PCI組態空間讀取PCI裝置類別和所選偵錯裝置的PCI VID,並嘗試載入名稱中具有這些標識符的模組,以建置PCI型 KDNET 擴充性模組的名稱。 如果您的裝置具有不是網路0x02類別的PCI類別程式代碼,則必須在 KDNET 擴充性模組的名稱中使用十六進位的正確PCI 類別程式代碼。 否則,winload 將無法正確載入您的模組。 _02_
每個名稱中的 是十六進位中網路類別裝置的PCI類別程式代碼。 您也可以從偵錯裝置的PCI組態空間找到並讀取此程式碼。
如果您有具有 DBG2 資料表專案的裝置,且不是 PCI 型裝置,則模組的命名慣例會有所不同。 DBG2 數據表偵錯裝置的命名慣例是kd_XXXX_YYYY.dll其中 XXXX 是十六進位 DBG2 數據表 PortType,而 YYYY 是 DBG2 數據表 PortSubtype 的十六進位數據表 PortSubtype。 Kd_8003_5143.dll是支持 net (0x8003) PortType 的收件匣 DLL,其子類型為 0x5143。 在此情況下,5143 是 Qualcomm PCI vid,因為這是在 Qualcomm USB 控制器上支援 KDNET,而針對 Net DBG2 數據表專案,PortSubtype 會定義為硬體廠商的 PCI VID。 請注意,您可以使用此命名慣例支援序列、USB 和其他 DBG2 資料表裝置。 以下是目前支援的 PortType 值十六進位:8000 用於序列裝置、1394 裝置為 8001、USB 裝置為 8002、NET 裝置為 8003。 請注意,序列和USB裝置的子類型必須保留Microsoft。 Microsoft會維護配置的序列和 USB 子類型清單。 如果現有的支持類型無法與硬體搭配使用,請傳送郵件至 kdnet@microsoft.com 以保留序列或USB子類型。
KDNET 擴充性匯入
以下是您可以從 KDNET 擴充性模組呼叫的例程清單。 請注意,所有這些例程都會傳遞至 KdInitializeLibrary 例程,而 kdnetextensibility.h 標頭會重新對應對這些例程的一般呼叫,以通過匯入數據表。 您的程式代碼必須透過匯入數據表呼叫這些專案,讓模組沒有匯入。 您不可以呼叫核心、HAL 或任何其他核心模組所導出的任何其他例程。 您只能呼叫這些例程。 這組例程已證明足以開發方塊 KDNET 擴充性模組中的所有 ,而且應該足以滿足一般案例。 如果您需要核心導出的其他例程,但不在此清單中,請傳送郵件來 kdnet@microsoft.com 說明您的案例,以及您需要哪些額外例程和原因。 請注意,只有在主要 Windows 發行週期中,如果完全如此,此列表才會新增至 。 請注意,這些例程大多會直接對應至核心或 HAL 所支援的 Windows 核心 API。 一或兩個是自定義的 KDNET 例程。
請務必正確在標頭中包含 kdnetextensibility.h,以便透過匯入數據表正確重新對應例程。 如果未這麼做,您的模組將會有匯入,且不受支援。
讀取寫入記憶體例程
下列例程應該用於讀取和寫入記憶體對應裝置記憶體。 這些具有相同的呼叫慣例,並對應至其對應的核心例程:READ_REGISTER_UCHAR、READ_REGISTER_USHORT、READ_REGISTER_ULONG、WRITE_REGISTER_UCHAR、WRITE_REGISTER_USHORT、WRITE_REGISTER_ULONG和 64 位平臺上,只會READ_REGISTER_ULONG64和WRITE_REGISTER_ULONG64。 所有裝置記憶體存取都應該透過這些例程來完成,因為它們可確保處理器不會重新排序讀取和寫入。 請注意,msdn.microsoft.com 記載 Windows CE Compact 2013 例程,這些例程會對應至這些例程的呼叫慣例。 不幸的是,它似乎NT例程並未記載,但呼叫慣例是相同的。
讀取IO埠例程
下列例程應該用於讀取和寫入裝置 IO 埠。 這些具有相同的呼叫慣例,並對應至其對應的核心例程:READ_PORT_UCHAR、READ_PORT_USHORT、READ_PORT_ULONG、WRITE_PORT_UCHAR、WRITE_PORT_USHORT和WRITE_PORT_ULONG。 所有裝置 IO 埠存取都應該透過這些例程來完成。 請注意,msdn.microsoft.com 記載 Windows CE Compact 2013 例程,這些例程會對應至這些例程的呼叫慣例。
其他例程
您可以呼叫下列額外的例程,而且應該使用指定的參數正常呼叫。 請注意,這樣做時,在適當地包含 kdnetextensibility.h 標頭時,會透過 KDNET 擴充性匯入數據表重新對應函式呼叫,因此您的模組中不需要明確匯入 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
);
請注意,PoSetHiberRange 函式只能從 KdSetHibernateRange 例程呼叫。 此外,大部分的 KDNET 擴充性模組都不需要呼叫 KeBugCheckEx、KdMapPhysicalMemory64 和 KdUnmapVirtualAddress。 另一方面,基本上所有的 KDNET 擴充性模組都需要呼叫 KdGetPhysicalAddress,以取得程式裝置 DMA 引擎所需的物理記憶體位址,而且許多都需要呼叫 KeStallExecutionProcessor、KdGetPciDataByOffset 和 KdSetPciDataByOffset。 最後兩個例程是用來存取裝置PCI設定空間。
KDNET 擴充性導出
以下是每個 KDNET 擴充性例程的簡短描述。 您必須實作封包型 KDNET 擴充性模組或序列型 KDNET 擴充性模組所需的所有例程。 以下是封包 KDNET 擴充性模組導出。
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;
}
呼叫此例程,以傳遞 KDNET 與此 KDNET 擴充性模組之間的匯入和匯出例程。 此例程應該驗證匯入和匯出數據表的版本必須是且受支援,如果不是,則失敗。 它應該在自己的全域記憶體中製作匯入數據表的複本。 它應該將它導出的例程寫入匯入數據表的 [匯出] 字段所指向的結構。 它也必須設定記憶體結構的 Length 欄位,這是傳遞至此例程之偵錯裝置描述元指標的一部分,以及支援硬體裝置所需的記憶體位元組數。
驗證匯入匯出計數
程式代碼必須檢查 Imports FunctionCount 是否符合 OS 中可用的專案,例如 - (KdNetExtensibilityImports->FunctionCount != KDNET_EXT_IMPORTS)
如果計數不相符,則傳回 STATUS_INVALID_PARAMETER
。
檢查計數可確保執行中的 Windows OS KDNET 版本(例如開機管理員/OS 載入器/Hypervisor/安全核心/NT OS 之間的相容性,這一切都連結至 KDNET 連結庫),以及用來建置目前 KDNET 擴充性模組的 WDK 版本。 WDK 和 OS 版本必須同步處理;否則,如果匯入/匯出計數器值變更,上述檢查將會失敗。 例如,如果 KDNET 擴充性介面新增了新的功能函式,則計數將不再相符。 為了確保它們相符,請一律使用符合裝載 KDNET 版本的 OS 的 WDK 版本。
自定義必要的記憶體
請注意,裝置會填入為調試程式選取的硬體。 如有需要,此例程應該根據裝置自定義所需的記憶體數量。 例如,支援 1Gig 和 10Gig 硬體的擴充性模組可以增加針對 10Gig 裝置要求的記憶體大小。 他們可以檢查偵錯裝置描述元的 DeviceID 欄位,判斷正在使用哪一個裝置。
KdInitializeLibrary 是唯一的導出
請注意,此例程會透過 winload 和 KDNET 在 KdInitSystem 呼叫期間呼叫。 請注意,這是 KDNET 擴充性模組導出的唯一例程。 這是放在 .def 檔案中的唯一例程。 KDNET 擴充性模組只有一個明確的匯出 – 此例程 – 且不會匯入。
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.
--*/
此例程由系統在休眠之前呼叫,以便能夠正確地向系統註冊 KDNET 擴充性模組所使用的程序代碼。 這可讓系統在休眠期間正確管理該記憶體,並從休眠中繼續。 (記憶體遲到,並提前載入,因為它將在恢復期間非常早地呼叫。
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.
--*/
呼叫此例程來初始化硬體。 當系統初始化時,每當系統從名為 KdShutdownController 的低功率狀態喚醒時,就會呼叫它。 此例程必須確定硬體已完成初始化,且已準備好在傳回之前傳送封包。 此例程應等候 PHY 出現,並建立連結。 請注意,如果沒有連接纜線,此例程不應無限期停止。 此例程會在 KDNET 與此擴充性模組之間共用的 KDNET 共用數據結構中設定連結速度和雙工。 它也會將硬體所使用的 MAC 位址寫入 KDNET 共用數據結構中 TargetMacAddress 所指向的位置。
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.
--*/
關鍵是,此例行等候直到所有仍在擱置的封包實際傳送到網路上。 此例程必須等到所有傳輸封包都已從主要記憶體中取出,並在關閉硬體上的傳輸之前,先在網路上出線。 傳送所有擱置的傳輸封包之後,此例程應該會完全關閉硬體。 當系統關閉時,也會呼叫此例程,以及當系統決定將偵錯傳輸電源管理為低電源狀態時呼叫。 當系統進入待命、休眠、睡眠和連線待命時,除了系統關機時,還可以呼叫此動作。
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.
--*/
此例程應該傳回支持硬體所需的所有記憶體所需的位元組數目。 這包括您的內容結構,以及接收和傳輸的所有封包緩衝區,以及任何硬體封包描述元和其他結構。 您必須在這裡回報您需要的 ALL 記憶體大小。 包含硬體對於封包、封包描述元或其他結構可能具有的對齊限制所需的任何額外記憶體。
請注意,當您的 KdInitializeLibrary 例程在偵錯裝置描述元中設定 [記憶體長度] 字段時,應該呼叫此例程。
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.
--*/
此例程會取得已接收但尚未處理的下一個可用封包。 它會傳回該封包的句柄。 句柄將用來藉由呼叫 KdGetPacketAddress 來取得封包的位址,以及呼叫 KdGetPacketLength 的長度。 封包和句柄必須保持可用且有效,直到呼叫 KdReleaseRxPacket 釋放封包為止。 此例程也會直接將封包地址和長度傳回給呼叫端。
如果目前沒有可用的封包,此例程必須立即傳回STATUS_IO_TIMEOUT。 此例程「不得」等候接收封包。 請注意,保留前 2 位的 Handle。 TRANSMIT_HANDLE和TRANSMIT_ASYNC都必須清楚。
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.
--*/
此例程會將與封包 Handle 相關聯的資源釋放回硬體,以便用來接收另一個封包。 每次對 KdGetRxPacket 的呼叫都會接著另一個呼叫 KdReleaseRxPacket,以及從 KdGetRxPacket 傳回的句柄。 請注意,不保證 KdReleaseRxPacket 會在 KdGetRxPacket 成功之後立即呼叫。 可能會先進行另一個 KdGetRxPacket 呼叫。 不過,每個成功的 KdGetRxPacket 呼叫,都會使用 KdReleaseRxPacket 呼叫釋放其資源。
此例程應該適當地設計硬體的程式,讓已發行的資源可用來接收另一個封包。
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.
--*/
此例程會取得下一個可用的傳輸資源,並傳回它們的句柄。 此句柄將用來呼叫 KdGetPacketAddress 和 KdGetPacketLength。 KdGetPacketAddress 傳回的封包地址將用來直接寫入封包的內容。 封包地址必須是封包的開頭,長度應該是可以寫入封包的最大位元組數目。 請注意,如果沒有可用的硬體資源,因為它們都已取得,而且尚未傳輸,則此例程應該立即傳回STATUS_IO_TIMEOUT。
TRANSMIT_HANDLE必須在傳回的句柄中設定。 請注意,前兩個句柄位會保留給TRANSMIT_ASYNC和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.
--*/
這個例程會將與傳遞的句柄相關聯的封包傳送到線路上。 請注意,Handle 中可能設定了額外的位,指出傳送是否為異步傳輸。 如果在句柄中設定了TRANSMIT_ASYNC旗標,則此例程應該將硬體程序設計為傳送封包,然後應該立即傳回,而不需要等待硬體完成傳送。 這表示傳輸期間發生的任何錯誤都會遺失。 這沒問題,根據設計,因為封包無論如何都可以在網路上遺失。 如果未在 Handle 中設定TRANSMIT_ASYNC旗標,則此例程必須等到封包在網路上傳送,而且應該傳回傳輸期間發生的任何錯誤。 請注意,當傾印檔案傳送至調試程式主機,或 Windows 網路封包透過 KDNET 從 KDNIC 傳送時,將會設定TRANSMIT_ASYNC。 傳送所有其他調試程式封包時,TRANSMIT_ASYNC將會清楚。
如果一組封包以 TRANSMIT_ASYNC設定為 TRUE 傳送,後面接著未設定TRANSMIT_ASYNC的封包,則硬體必須等到未設定旗標的封包,實際上也會傳送,即使這表示也必須等候先前的異步封包傳送。
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.
--*/
這個例程會傳回與所傳遞句柄相關聯之封包第一個字節的指標。 請注意,Handle 會針對傳輸封包設定TRANSMIT_HANDLE位,而接收封包的TRANSMIT_HANDLE位則為清除。 傳回的指標應該是可由處理器讀取或寫入的 Windows 虛擬位址。 此位址應該落在保留給 KDNET 擴充性模組的記憶體區塊中,此模組會在偵錯裝置描述元記憶體結構中傳遞。 (請注意,KDNET 擴充性模組絕對不應該在存取該記憶體時,使用 KdInitializeLibrary 中所要求的記憶體大小。區塊結尾的任何額外記憶體都會保留供 KDNET 使用,且不得由 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.
--*/
此例程會傳回與所傳遞句柄相關聯之封包的位元組長度。 請注意,Handle 會針對傳輸封包設定TRANSMIT_HANDLE位,而接收封包的TRANSMIT_HANDLE位則為清除。 對於傳輸封包,此長度應該是可以寫入封包的最大位元元組數目。 對於接收封包,此長度應該是所接收封包中實際位元組數目。
偵錯 KDNET 擴充性模組
若要偵錯 KDNET 擴充性模組,您需要從目標計算機上的提升許可權命令提示字元執行下列 bcdedit 命令。
首先,最重要的是,您必須執行下列兩個命令,以確保 Winload 會允許重複開機失敗,而不會進入調試程式並防止正常開機的特殊失敗路徑。 執行這些命令可讓您使用新的位重複重新啟動計算機,並偵錯這些新位,而不會發生問題。
Bcdedit -set {current} BootStatusPolicy IgnoreAllFailures
Bcdedit -set {current} RecoveryEnabled No
假設您會在目標電腦上的 com1 上使用序列偵錯來偵錯擴充性模組,請執行下列動作。
bcdedit -dbgsettings serial debugport:1 baudrate:115200
這會在 com1 上將預設偵錯傳輸設定為 115200 baud 串行化。 這些設定也會用於開機偵錯。
bcdedit -debug on
這可啟用核心偵錯。
bcdedit -bootdebug on
這可在winload.exe上啟用開機偵錯,您將用來偵錯早期核心初始化,包括 KDNET 擴充性模組。
bcdedit -set kerneldebugtype net
這會將核心偵錯類型強制為 net,不論預設偵錯傳輸設定為何。 這會導致winload.exe載入kdnet.dll做為核心偵錯傳輸。
bcdedit -set kernelbusparams b.d.f
其中 b 是總線號碼,d 是裝置號碼,而 f 是函式號碼 , 全都是小數點 – 您撰寫 KDNET 擴充性模組的硬體。 這些數位將取決於硬體所在的PCI位置。 您可以在 Windows 裝置管理器中尋找網路裝置的裝置屬性頁面中的位置字串,以找到這些位置字串。 開啟 Windows 設備管理器、按兩下網路裝置、尋找您的裝置、按兩下裝置,然後在開啟的視窗中應該會有位置:字段,其中包含PCI總線上硬體的總線、裝置和功能。 如果您有導致該資訊遮罩的公交車司機,則必須從司機或其他方式判斷位置。
這會強制核心總線參數 b.d.f – 強制選取該特定裝置做為核心偵錯裝置。
bcdedit -set kernelhostip N
其中 N 是由下列公式所決定。 如果您的主機調試程式計算機具有 w.x.y.z 的 IPv4 位址,則 N = (w0x01000000) + (x0x00010000) + (y0x00000100) + (z0x00000001)。 必須在十進位的命令行上指定 N,而不是十六進位。 實際上,您會取得 IPv4 位址的每個位元組,並串連它(十六進位),以在十六進位中建置 32 位數位,然後將它轉換成十進位。
bcdedit -set kernelport N
其中 N 是 50000 或一些不會在您的內部網路上封鎖的其他埠。
這會強制 KDNET 使用埠 N 作為網路偵錯埠。
bcdedit -set kernelkey 1.2.3.4
這會強制 KDNET 偵錯金鑰為 1.2.3.4。 1.2.3.4 在網路密鑰上不是安全或唯一的。 若要保護目標計算機的安全,必須在主計算機與目標計算機之間移動的封包加密。 強烈建議您使用自動產生的加密金鑰。 如需詳細資訊,請參閱 自動設定 KDNET 網路核心偵錯。
bcdedit -set kerneldhcp on
這會強制將 KDNET 核心 dhcp 設定設為開啟。
使用下列命令行在調試程式主計算機上執行調試程式,假設您使用 com1 作為主電腦上的序列偵錯埠:
windbg -d -k com:port=com1,baud=115200
這會執行調試程式,並在windbg開機調試程式第一次與主計算機通訊時造成中斷。
然後執行 來重新啟動目標計算機
shutdown -r -t 0
當調試程式中斷為windbg時,請確定您會收到載入 winload 的符號。 (可能需要設定 .sympath,並執行 .reload)。 接著,執行 x winload!*deb*tra*
。 列出的其中一個符號會類似 BdDebugTransitions。
然後執行 ed winload!BdDebugTransitions 1
,但請務必使用正確的符號名稱。
然後執行 , bu winload!blbdstop
以設定斷點。
然後點擊 g
去。
您應該在 winload 中中斷!BlBdStop。
然後,執行下列命令。
bu nt!KdInitSystem
bu kdnet!KdInitialize
bu kdstub!KdInitializeLibrary
請注意,在 KDNET 擴充性模組中設定斷點時,最有可能使用 kdstub,如果這無法運作,請使用
bu kd_YY_XXXX!KdInitializeLibrary
其中 YY 是 PCI 類別,XXXX 是 PCI VID。 (例如:使用 KDNET 擴充性模組的名稱。
通常,在調試程式中,您必須使用 kdstub,而不是使用擴充性模組的實際名稱。
然後執行 bl
以列出斷點。 請確定斷點已就緒(它們應該都位於它們旁邊)。
然後叫用 g
。 您應該按下 nt!KdInitSystem 斷點。
再次點擊 g
,您應該點擊 kdnet!KdInitialize
再次點擊 g
,您應該在 KdInitializeLibrary 的自己的課程模組中叫用斷點。
接著,您可以在 InitializeController 例程以及所有其他例程上設定斷點,並逐步執行程式碼。
一旦您逐步執行 KdInitializeLibrary、hit g,而且如果您在 InitializeController 例程上設定斷點,就會在下一次叫用。
完成後,請確定您已在 KdGetTxPacket、KdSendTxPacket、KdGetRxPacket、KdReleaseRxPacket 例程上設定斷點,然後再次叫用 g,而且這些例程會在開機期間由 KDNET 完成的網路初始化過程中執行。
您可能需要將暫存程式代碼新增至 KdInitializeLibrary 或 KdInitializeController 例程,以確保呼叫所有例程,以便逐步執行所有程序代碼。 (KdShutdownController 例如,在正常運作時,啟動期間不會呼叫它,因此您必須從暫存程式代碼明確呼叫它,以便您可以逐步執行,並確定它正確無誤。
一旦您逐步完成所有程序代碼,並確信其正確無誤,請重新啟動目標,但請勿設定 winload!BdDebugTransitions 旗標為 true(將它預設為零)。
然後在您的主機調試程式計算機上執行另一個核心調試程序實例。
Windbg -d -k net:port=50000,key=1.2.3.4
讓目標電腦開機,而且應該透過網路連線到核心調試程式的其他實例。
然後在核心調試程式中執行命令,並確定其運作正常,然後讓目標繼續開機,並確定您稍後可以中斷並執行命令。
注意
在 winload 中設定偵錯轉換旗標,保證 Windows 不會開機。 如果您嘗試讓 Windows 在設定該旗標之後完成開機,Windows 只會當機或停止回應。 如果您想要 Windows 順利開機,則無法設定該偵錯轉換旗標。 設定旗標可讓您對程式代碼進行偵錯,並藉由在調試程式中逐步執行,確認其正確無誤,但最終您不需要設定旗標,以便確認偵錯正常開機時可正常運作。 這表示您無法在正常開機時逐步執行程式代碼,事實上,在硬體上啟用偵錯的情況下,Windows 正常執行時,您的 KDNET 擴充性模組無法偵錯。 任何嘗試使用核心調試程序進行偵錯,都會導致計算機當機。 (您無法在核心偵錯路徑中執行的程式代碼中設定斷點,因為它會導致無限重新進入、一個吹爆的堆疊和重新啟動。
多個實體函式 - 2PF
除了 KDNET 擴充性之外,KDNET 還支援在支援的 NIC 上使用多個實體函式 (PFs) 進行核心偵錯,方法是分割 PCI 組態空間。 鼓勵網路卡廠商啟用此功能的支援。 如需詳細資訊,請參閱 調試程式 2PF KDNET 迷你埠網路驅動程序支援。