Our article Master of the Obvious - MDLs are Lists that Describe Memory mentioned MDLs are opaque. In this context opaque means the MDL data structure is not documented and subject to change, thus preventing you from directly accessing the fields within the MDL. So, how can you manipulate MDLs if they are not documented? This follow-up article addresses this question by listing and describing the routines (or methods if you want to think of the MDL as a Memory Manager object) used to manipulate MDLs.
This routine is used by the caller to get a non-paged kernel-mode virtual address (KVA) for the physical pages described by the MDL. When this routine is called, the pages must be in non-paged pool or already locked in physical memory by MmProbeAndLockPages. The KVA returned for this MDL is valid until the pages are unlocked. All callers of this routine mapping a given MDL will receive the same KVA. Note that this routine can return NULL if the system does not have enough system page table entries (SPTEs) available.
This routine returns the length in bytes of the buffer described by this MDL. If this MDL is to be used in a data transfer, this size is not the length of the requested transfer, since the MDL may describe more memory then the caller wants to transfer or receive. The length of the transfer is included in the parameters area of your stack location!
This routine returns the byte offset to the location where the data starts in the first page of the buffer described by the MDL. Since not all buffers are necessarily page aligned, this function is used to determine the alignment of the buffer.
This routine returns a pointer to the beginning of the array of physical page numbers associated with the MDL. This function is not used frequently, and is generally not appropriate for use in setting up a DMA transfer.
This routine, which is almost exclusively used in setting up DMA transfers, returns the base virtual address of the buffer described by the MDL. It's important to realize that the virtual address returned by this function may not be valid in the current thread context. However, when setting up for a DMA transfer, the returned address is passed to GetScatterGatherList, which uses it to determine the starting offset to the physical pages comprising the MDL.
This routine initializes an MDL and is typically used in conjunction with MmPrepareMdlForReuse to reinitialize the MDL for its next usage. If the caller uses IoAllocateMdl to allocate an MDL, then the MDL has already been initialized. In this case, the MmInitializeMdl must not be called.
This routine reinitializes a caller-allocated MDL. If the pages described by this MDL were previously locked, they are unlocked. This routine is called by drivers that allocate a set of MDLs that they repeatedly use and reuse.
This routine is used to build an MDL that describes a portion of a buffer that is described by another MDL. This routine should be used for a driver that must split a transfer to multiple transfers.
This routine is used to allocate an MDL that is large enough to describe the pages of the buffer and length used as input to this routine.
This routine is called to return an MDL that was allocated with IoAllocateMDL. It is assumed that if this MDL mapped with MmProbeAndLockPages, it has been unmapped via a call to MmUnlockPages.
This routine takes as input a kernel virtual address and length describing memory residing in non-paged pool and returns an MDL that describes its underlying pages.