reactos/sdk/lib/drivers/wdf/shared/support/km/fxrequestbufferkm.cpp
Victor Perevertkin 8a978a179f
[WDF] Add Windows Driver Framework files
Takern from Microsoft GitHub repo:
d9c6040fe9

Licensed under MIT
2020-11-03 00:06:26 +03:00

277 lines
7.8 KiB
C++

/*++
Copyright (c) Microsoft. All rights reserved.
Module Name:
FxRequestBufferKm.cpp
Abstract:
This module implements a memory union object
Author:
Environment:
Kernel mode only
Revision History:
--*/
#include "FxSupportPch.hpp"
extern "C" {
#include "FxRequestBufferKm.tmh"
}
_Must_inspect_result_
NTSTATUS
FxRequestBuffer::GetOrAllocateMdl(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__deref_out_opt PMDL* Mdl,
__inout PMDL* MdlToFree,
__inout PBOOLEAN UnlockWhenFreed,
__in LOCK_OPERATION Operation,
__in BOOLEAN ReuseMdl,
__inout_opt size_t* SizeOfMdl
)
/*++
Routine Description:
This function attempts to reuse the passed-in MDL (if any) or allocates
a new MDL if reuse flag isn't passed-in or if the existing MDL isn't
big enough.
Arguments:
Return Value:
FxDriverGlobals - Driver globals
Mdl - on return it contains the MDL allocated/reused
MdlToFree - pointer to any MDL
* to be reused, if the size is <= current size, or
* freed and set to newly allocated MDL
UnlockWhenFreed - whether to unlock pages when freeing MDL
(if FALSE, MDL may represent just MDL buffer but the pages
might have already been unlocked)
Operation - Operation to pass to MmLockPages
ReuseMdl - whether to reuse *MdlToFree
Please note that this can be FALSE even when MDL is supplied
SizeOfMdl - on input contains size of *MdlToFree,
on return contains size of *Mdl
Remarks:
*MdlToFree is modified only when this function frees the passed in MDL
Otherwise it leaves it untouched. Caller is responsible for storing
properly initialized value and/or freeing what's stored in the value.
--*/
{
PVOID pBuf;
NTSTATUS status;
ULONG length;
BOOLEAN oldUnlockValue;
pBuf = NULL;
oldUnlockValue = *UnlockWhenFreed;
//
// Format functions that use this helper call
// FxRequestBase::ValidateTarget which calls ContextReleaseAndRestore
// which unlocks any locked pages.
//
// Hence pages must already be unlocked now. Let's assert that.
//
// This condition needs to be true since we unconditionally set
// *UnlockWhenFreed to FALSE just below.
//
ASSERT (oldUnlockValue == FALSE);
*UnlockWhenFreed = FALSE;
//
// Even if ReuseMdl is not true, SizeOfMdl may be supplied to store
// the size of allocated MDL to be used later
//
ASSERT(ReuseMdl ? (SizeOfMdl != NULL && *MdlToFree != NULL) : TRUE);
switch (DataType) {
case FxRequestBufferUnspecified:
*Mdl = NULL;
//
// We should not set *MdlToFree to NULL as *MdlToFree might have a valid
// MDL which we should not overwrite with NULL without freeing it
//
return STATUS_SUCCESS;
case FxRequestBufferMemory:
if (u.Memory.Offsets != NULL) {
pBuf = WDF_PTR_ADD_OFFSET(u.Memory.Memory->GetBuffer(),
u.Memory.Offsets->BufferOffset);
}
else {
pBuf = u.Memory.Memory->GetBuffer();
}
// || ||
// \/ \/ fall through
case FxRequestBufferBuffer:
if (pBuf == NULL) {
pBuf = u.Buffer.Buffer;
}
length = GetBufferLength();
status = GetOrAllocateMdlWorker(FxDriverGlobals,
Mdl,
&ReuseMdl,
length,
pBuf,
SizeOfMdl,
oldUnlockValue,
MdlToFree
);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGAPIERROR,
"Couldn't allocate memory for MDL of length 0x%x %!STATUS!", length, status);
return status;
}
//
// If we are reusing the MDL we need to initialize it with current
// buffer.
//
if (ReuseMdl == TRUE) {
Mx::MxInitializeMdl(*Mdl, pBuf, length);
}
status = FxProbeAndLockWithAccess(*Mdl, KernelMode, Operation);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGAPIERROR,
"Couldn't lock pages for MDL 0x%p %!STATUS!", *Mdl, status);
//
// Free MDL only if it was not reused.
//
if (ReuseMdl == FALSE) {
FxMdlFree(FxDriverGlobals, *Mdl);
}
*Mdl = NULL;
return status;
}
*UnlockWhenFreed = TRUE;
*MdlToFree = *Mdl;
return STATUS_SUCCESS;
case FxRequestBufferMdl:
*Mdl = u.Mdl.Mdl;
//
// We should not set *MdlToFree to NULL as *MdlToFree might have a valid
// MDL which we should not overwrite with NULL without freeing it
//
return STATUS_SUCCESS;
case FxRequestBufferReferencedMdl:
if (u.RefMdl.Offsets == NULL ||
(u.RefMdl.Offsets->BufferOffset == 0 && u.RefMdl.Offsets->BufferLength == 0)) {
*Mdl = u.RefMdl.Mdl;
//
// We should not set *MdlToFree to NULL as *MdlToFree might have a valid
// MDL which we should not overwrite with NULL without freeing it
//
}
else {
//
// Do not use MmGetSystemAddressForMdlSafe because StartVa could be
// in UM while MappedVa (obviously) is in KM. Since
// IoBuildPartial Mdl basically uses
// pBuf - MmGetMdlVirtualAddress(SrcMdl) to compute offset, if one
// VA is in UM (e.g. MmGetMdlVirtualAddress(SrcMdl)), you get the
// (drastically) wrong offset.
//
pBuf = Mx::MxGetMdlVirtualAddress(u.RefMdl.Mdl);
ASSERT(pBuf != NULL);
pBuf = WDF_PTR_ADD_OFFSET(pBuf, u.RefMdl.Offsets->BufferOffset);
//
// GetBufferLength will compute the correct length with the given offsets
//
length = GetBufferLength();
status = GetOrAllocateMdlWorker(FxDriverGlobals,
Mdl,
&ReuseMdl,
length,
pBuf,
SizeOfMdl,
oldUnlockValue,
MdlToFree
);
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(
FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGAPIERROR,
"Couldn't allocate memory for MDL of length 0x%x %!STATUS!", length, status);
return status;
}
Mx::MxBuildPartialMdl(
u.RefMdl.Mdl,
*Mdl,
pBuf,
length
);
*MdlToFree = *Mdl;
}
return STATUS_SUCCESS;
default:
*Mdl = NULL;
//
// We should not set *MdlToFree to NULL as *MdlToFree might have a valid
// MDL which we should not overwrite with NULL without freeing it
//
return STATUS_INVALID_PARAMETER;
}
}
VOID
FxRequestBuffer::SetMemory(
__in IFxMemory* Memory,
__in PWDFMEMORY_OFFSET Offsets
)
{
PMDL pMdl;
pMdl = Memory->GetMdl();
if (pMdl != NULL) {
DataType = FxRequestBufferReferencedMdl;
u.RefMdl.Memory = Memory;
u.RefMdl.Offsets = Offsets;
u.RefMdl.Mdl = pMdl;
}
else {
DataType = FxRequestBufferMemory;
u.Memory.Memory = Memory;
u.Memory.Offsets = Offsets;
}
}