Geoff Chappell, Software Analyst
This function gets or creates an adapter object for a given device’s DMA operations.
PDMA_ADAPTER IoGetDmaAdapter ( PDEVICE_OBJECT DeviceObject, PDEVICE_DESCRIPTION DeviceDescription, ULONG *NumberOfMapRegisters);
The DeviceObject argument is the address of a structure that represents the device that seeks to perform DMA. This argument can be NULL.
The DeviceDescription argument is the address of a structure that describes the device’s DMA requirements.
The NumberOfMapRegisters argument is the address of a variable that is to receive the maximum number of map registers that the device can use for any DMA operation.
If successful, the function returns the address of a structure that represents the DMA adapter. The function returns NULL to indicate failure.
The IoGetDmaAdapter function is exported by name from the kernel in version 5.0 and higher.
The function is documented. For Microsoft’s description of the implied PnP request, look to the documentation of IRP_MN_QUERY_INTERFACE.
The DMA adapter may be found in either of two ways. One is to send a PnP request through the given device object to see if the bus driver has its own routine for getting a DMA adapter. The other is to default to the HAL, essentially through the obsolete HalGetAdapter function.
The PnP method is possible only if a device object is given. Moreover, the device object must be a physical device object (PDO). This means specifically that it must be associated with a device node that is fully created and is not about to be destroyed. If this is not true of the given device object, the function raises the bug check PNP_DETECTED_FATAL_ERROR (0xCA). The first bug-check argument is 2, the second is the address of the device object, and the remaining two are zero.
In Windows 10, the function first calls whatever routine it finds as the HalDmaLinkDeviceObjectByToken member of the HalPrivateDispatchTable. The latter is a jump table in the kernel, but it is exported and might be diverted by anyone who knows the HAL_PRIVATE_DISPATCH type. The routine is passed a token and DeviceObject. The token happens to be the address of the current thread’s KTHREAD, but it is likely intended that the routine treat this as opaque. When the function is done, whether it succeeds or fails, and whether it turns out to default to the HAL, it calls the routine again, passing the same token but with NULL instead of DeviceObject.
If the InterfaceType member of the DEVICE_DESCRIPTION is either InterfaceTypeUndefined or PnPBus, the function substitutes whatever interface type is obtained by calling the IoGetDeviceProperty function for DevicePropertyLegacyBusType. If this fails, the function substitutes a default, which is presently Isa. (The substitution is done in a copy of the given DEVICE_DESCRIPTION, as if the given structure is read-only.)
The function sends a synchronous IRP_MJ_PNP request to the top of the given device object’s I/O stack. Failure to build the request is failure for the function. If the request fails, the function defaults to calling the HAL (see below). The PnP request is specifically an IRP_MN_QUERY_INTERFACE for the interface represented by GUID_BUS_INTERFACE_STANDARD, asking for version 1 of the interface, expecting some driver in the stack to provide a BUS_INTERFACE_STANDARD structure. Three members in this structure matter: Context, InterfaceDereference and GetDmaAdapter. The last, of course, is the point. If GetDmaAdapter is not NULL, the function calls it to get the DMA adapter object, passing the Context member, the address of the DEVICE_DESCRIPTION structure (which may be a slightly modified copy) and the NumberOfMapRegisters argument. The Context and InterfaceDereference members always matter, since the function passes the former to the latter when done with the interface.
If the DeviceObject argument is NULL or the IRP_MN_QUERY_INTERFACE request fails or the GetDmaAdapter routine returns NULL, then the function defaults to calling the HAL. The ordinary effect is of reverting to the obsolete function HalGetAdapter, passing the DeviceDescription and NumberOfMapRegisters arguments.
A less ordinary, perhaps even unusual, effect is possible because the call does not go directly to the function that the HAL exports as HalGetAdapter. It goes instead through the HalGetDmaAdapter member of the HalDispatchTable. The latter is a jump table in the kernel, but it is exported and its type, HAL_DISPATCH, is public. Though the dispatch table is ordinarily set by the HAL, surely the only point to it is that its entries can be modified, and the call to the HAL for getting a DMA adapter object can therefore be filtered.
The function is implemented in paged code and is to be called only at PASSIVE_LEVEL.