Geoff Chappell - Software Analyst
The HAL_DISPATCH structure is a table of pointers to optional HAL functionality. The kernel keeps the one instance of this table. It’s in the kernel’s read-write data section and its address is exported as HalDispatchTable. The table initially has the kernel’s built-in implementations of most (but not all) functions. Many are trivial. Some are substantial. The HAL overrides some. No known HAL overrides all. Functionality that has no meaning to a particular HAL is left to the kernel’s default (and HAL programmers are spared from writing even dummy code for nothing that matters to them). Moreover, since the address is exported, rather than communicated specifically to the HAL, it seems to have been intended all along that the functionality is exposed to other kernel-mode modules such as drivers not only for them to call but also to override further.
Neither the HAL_DISPATCH nor the HalDispatchTable are formally documented. Or so I wrote in 2016. Since then Microsoft has published documentation online. What’s there today, 29th October 2022, is dated 05/31/22 in plain sight and 03/07/2022 in an HTML <meta> tag named “ms.date”. Recent or not, it’s a superb example of documentation that exists for no more reason than to allow that someone might say its subject is documented—not usefully, but documented nonetheless. It adds nothing to a comment-free C-language definition. Indeed, it starts with a comment-free C-language definition and then the remainder of this so-called documentation is just a (possibly automated) restatement in plain language. For instance, what’s expressed in C as
pHalQuerySystemInformation HalQuerySystemInformation;
is then re-expressed in English as
Defines the pHalQuerySystemInformation member HalQuerySystemInformation.
This, repeated for all the structure’s members, is the whole of the documentation. Perhaps it’s a placeholder for work in progress.
Whatever’s to be made of what a supposedly more open Microsoft now passes off as documentation, the HAL_DISPATCH and HalDispatchTable have always been semi-documented: a C-language definition appears in every NTDDK.H even from as far back as the Device Driver Kit (DDK) for Windows NT 3.51. In each DDK or Windows Driver Kit (WDK), the definition is true for the Windows version that the kit is released for, but with no explicit indication that the definition might not be correct for other versions. This would be unremarkable if the history were just of extending the structure and incrementing the Version member at the very beginning. Instead, members have been added, or their types changed, without increasing the Version. One change of Version removes a member, thus shifting all subsequent ones. That said, since then, meaning Windows 7, the structure has not changed up to and including the 2004 edition of Windows 10, and may now be stable.
Microsoft’s C-language definitions don’t list what to expect as the Version member for each Windows version or what to expect in the structure for any given Version. The following table shows what correspondence is known from inspection:
Version | Windows Versions | Size (x86) | Size (x64) |
---|---|---|---|
1 | 3.51 | 0x28 | |
4.0 | 0x34 | ||
2 | 5.0 | 0x44 | |
3 | 5.1 to 6.0 | 0x58 | 0xB0 |
4 | 6.1 to 2004 | 0x5C | 0xB8 |
These sizes in the preceding table, and the offsets, types and names in the table that follows, are from Microsoft’s C-language definitions in NTDDK.H, checked against (some) inspection of what the kernel actually does have as its HalDispatchTable. It is left as understood that x64 offsets are meaningful only for those versions that have x64 builds, i.e., 5.2 from Windows Server 2003 SP1, and higher.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 |
ULONG Version; |
3.51 and higher |
0x04 | 0x08 |
NTSTATUS (*HalQuerySystemInformation) ( HAL_QUERY_INFORMATION_CLASS, ULONG, PVOID, ULONG *); |
3.51 and higher |
0x08 | 0x10 |
NTSTATUS (*HalSetSystemInformation) ( HAL_SET_INFORMATION_CLASS, ULONG, PVOID); |
3.51 and higher |
0x0C |
NTSTATUS (*HalQueryBusSlots) ( INTERFACE_TYPE, ULONG, ULONG, ULONG *, ULONG *); |
3.51 only | |
0x18 |
NTSTATUS (*HalQueryBusSlots) ( BUS_HANDLER *, ULONG, ULONG *, ULONG *); |
4.0 and higher | |
0x10 |
NTSTATUS (*HalSlotControl) ( INTERFACE_TYPE, ULONG, ULONG, DEVICE_OBJECT *, ULONG, PVOID, ULONG *, PVOID, PSLOT_CONTROL_COMPLETION); |
3.51 only | |
NTSTATUS (*HalDeviceControl) ( DEVICE_HANDLER_OBJECT *, DEVICE_OBJECT *, ULONG, PVOID, ULONG *, PVOID, PDEVICE_CONTROL_COMPLETION); |
4.0 only | ||
0x20 |
ULONG Spare1; |
5.0 and higher | |
0x14 | 0x28 |
VOID (FASTCALL *HalExamineMBR) ( DEVICE_OBJECT *, ULONG, ULONG, PVOID *); |
3.51 and higher |
0x18 (3.51 to 6.0) | 0x30 (5.2 to 6.0) |
VOID (FASTCALL *HalIoAssignDriveLetters) ( LOADER_PARAMETER_BLOCK *, STRING *, UCHAR *, STRING *); |
3.51 to 6.0 |
0x1C (3.51 to 6.0); 0x18 |
0x38 (5.2 to 6.0); 0x30 |
NTSTATUS (FASTCALL *HalIoReadPartitionTable) ( DEVICE_OBJECT *, ULONG, BOOLEAN, DRIVE_LAYOUT_INFORMATION **); |
3.51 and higher |
0x20 (3.51 to 6.0); 0x1C |
0x40 (5.2 to 6.0); 0x38 |
NTSTATUS (FASTCALL *HalIoSetPartitionInformation) ( DEVICE_OBJECT *, ULONG, ULONG, ULONG); |
3.51 and higher |
0x24 (3.51 to 6.0); 0x20 |
0x48 (5.2 to 6.0); 0x40 |
NTSTATUS (FASTCALL *HalIoWritePartitionTable) ( DEVICE_OBJECT *, ULONG, ULONG, ULONG, DRIVE_LAYOUT_INFORMATION *); |
3.51 and higher |
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x28 (4.0 to 6.0); 0x24 |
0x50 (5.2 to 6.0); 0x48 |
BUS_HANDLER * (FASTCALL *HalReferenceHandlerForBus) ( INTERFACE_TYPE, ULONG); |
4.0 and higher |
0x2C (4.0 to 6.0); 0x28 |
0x58 (5.2 to 6.0); 0x50 |
VOID (FASTCALL *HalReferenceBusHandler) ( BUS_HANDLER *); |
4.0 and higher |
0x30 (4.0 to 6.0); 0x2C |
0x60 (5.2 to 6.0); 0x58 |
VOID (FASTCALL *HalDereferenceBusHandler) ( BUS_HANDLER *); |
4.0 and higher |
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x34 (5.0 to 6.0); 0x30 |
0x68 (5.2 to 6.0); 0x60 |
NTSTATUS (*HalInitPnpDriver) ( VOID); |
5.0 and higher | |
0x38 (5.0 to 6.0); 0x34 |
0x70 (5.2 to 6.0); 0x68 |
NTSTATUS (*HalInitPowerManagement) ( PM_DISPATCH_TABLE *, PM_DISPATCH_TABLE *); |
5.0 and higher | |
0x3C (5.0 to 6.0); 0x38 |
0x78 (5.2 to 6.0); 0x70 |
DMA_ADAPTER * (*HalGetDmaAdapter) ( PVOID, DEVICE_DESCRIPTION *, ULONG *); |
5.0 and higher | no default |
0x40 (5.0 to 6.0); 0x3C |
0x80 (5.2 to 6.0); 0x78 |
NTSTATUS (*HalGetInterruptTranslator) ( INTERFACE_TYPE, ULONG, INTERFACE_TYPE, USHORT, USHORT, TRANSLATOR_INTERFACE *, ULONG *); |
5.0 and higher |
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x44 (5.1 to 6.0); 0x40 |
0x88 (5.2 to 6.0); 0x80 |
NTSTATUS (*HalStartMirroring) ( VOID); |
5.1 and higher |
0x48 (5.1 to 6.0); 0x44 |
0x90 (5.2 to 6.0); 0x88 |
NTSTATUS (*HalEndMirroring) ( ULONG); |
5.1 and higher |
0x4C (5.1 to 6.0); 0x48 |
0x98 (5.2 to 6.0); 0x90 |
NTSTATUS (*HalMirrorPhysicalMemory) ( PHYSICAL_ADDRESS, LARGE_INTEGER); |
5.1 and higher |
0x50 (5.1 to 6.0); 0x4C |
0xA0 (5.2 to 6.0); 0x98 |
VOID (*HalEndOfBoot) ( VOID); |
5.1 and higher |
0x54 (5.1 to 6.0); 0x50 |
0xA8 (5.2 to 6.0); 0xA0 |
NTSTATUS (*HalMirrorVerify) ( PHYSICAL_ADDRESS, LARGE_INTEGER); |
5.1 and higher |
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x54 | 0xA8 |
PVOID (*HalGetCachedAcpiTable) ( ULONG, PCSTR, PCSTR); |
6.1 and higher | no default |
0x58 | 0xB0 |
VOID (*HalSetPciErrorHandlerCallback) ( PCI_ERROR_HANDLER_CALLBACK); |
6.1 and higher | no default |
All non-obvious types in the preceding tables are structures or enumerations except for the following function pointers:
typedef VOID (*PSLOT_CONTROL_COMPLETION) (SLOT_CONTROL_CONTEXT *); typedef VOID (*PDEVICE_CONTROL_COMPLETION) (DEVICE_CONTROL_CONTEXT *); typedef VOID (*PCI_ERROR_HANDLER_CALLBACK) (VOID);
Of course, almost all members of the HAL_DISPATCH are function pointers, and Microsoft’s NTDDK.H defines types for them too. Those that I use here are just the ones that can be given as arguments: function pointers in function pointers get just a bit too complicated for easy presentation.