/*++ Copyright (c) Microsoft Corporation Module Name: FxRequestBase.hpp Abstract: This is the base class which request objects derive from for the driver frameworks. The request base object wraps the IRP, containing persistent information required by the driver frameworks. Author: Environment: Both kernel and user mode Revision History: --*/ #ifndef _FXREQUESTBASE_H_ #define _FXREQUESTBASE_H_ #include "fxrequestcallbacks.hpp" #define WDF_REQUEST_REUSE_MUST_COMPLETE 2 // // COMPLETED - set when the request's i/o completion routine has executed // // PENDED - set when the request has been put onto the target's CSQ // // TIMER_SET - set when a timer has been queued along with sending the request // down to the target // // CANCELLED_FROM_TIME - set by the timer to indicate that the request was // cancelled by the timer DPC // // IGNORE_STATE - set when the request is going to be sent ignoring the // state of the target. // enum FxRequestTargetFlags { FX_REQUEST_COMPLETED = 0x01, FX_REQUEST_PENDED = 0x02, FX_REQUEST_TIMER_SET = 0x04, FX_REQUEST_CANCELLED_FROM_TIMER = 0x08, FX_REQUEST_IGNORE_STATE = 0x10, }; // // internal private constraints // #define WDF_REQUEST_SEND_INTERNAL_OPTION_FAIL_ON_PEND (0x80000000) #define WDF_REQUEST_INTERNAL_CONSTRAINTS_VALID_FLAGS \ (WDF_REQUEST_SEND_INTERNAL_OPTION_FAIL_ON_PEND) // // Completion event callback prototype // typedef VOID (*PFN_COMPLETE_COPY_ROUTINE)( __in FxIoTarget* This, __in FxRequest* Request, __in_opt FxRequestContext* Context ); struct FxRequestTimer : public FxStump { MxTimer Timer; }; enum FxRequestAllocationSource { REQUEST_ALLOCATED_FROM_IO = 0, // Irp came from the I/O package REQUEST_ALLOCATED_INTERNAL = 1, // Irp was allocated internally and should be freed by the request REQUEST_ALLOCATED_DRIVER = 2, // Irp was given to the request by the driver writer }; enum FxRequestIrpOwnership { FxRequestOwnsIrp = 1, FxRequestDoesNotOwnIrp, }; // begin_wpp config // CUSTOM_TYPE(FxRequestIrpOwnership, ItemEnum(FxRequestIrpOwnership)); // end_wpp enum FxRequestConstructorCaller { FxRequestConstructorCallerIsFx = 1, FxRequestConstructorCallerIsDriver, }; // // These defines are for VerifierFlags // enum FxRequestVerifierFlags { // Request has been passed to the Driver FXREQUEST_FLAG_DRIVER_OWNED = 0x0001, // Request was returned as a "Tag" request to WdfIoQueuePeekNextRequest. FXREQUEST_FLAG_TAG_REQUEST = 0x0002, // Request has been forwarded from one queue to another FXREQUEST_FLAG_FORWARDED = 0x0004, // Request is being EvtIoDefault to the driver FXREQUEST_FLAG_DRIVER_DISPATCH = 0x0008, // The driver has specified the request as cancelable FXREQUEST_FLAG_DRIVER_CANCELABLE = 0x0010, FXREQUEST_FLAG_DRIVER_INPROCESS_CONTEXT = 0x0020, // The request has been cancelled FXREQUEST_FLAG_CANCELLED = 0x0040, // the next stack location has been formatted FXREQUEST_FLAG_FORMATTED = 0x0080, // the request has been sent on an I/O target and is in the target's sent list FXREQUEST_FLAG_SENT_TO_TARGET = 0x0100, // used to make sure the driver stop acknowledges in the context of EvtIoStop FXREQUEST_FLAG_DRIVER_IN_EVTIOSTOP_CONTEXT = 0x0200, // used to indicate whether the Reserved Request is in use or on Free list FXREQUEST_FLAG_RESERVED_REQUEST_ASSOCIATED_WITH_IRP = 0x0400, }; enum FxRequestBaseFlags { FxRequestBaseSystemMdlMapped = 0x1, FxRequestBaseOutputMdlMapped = 0x2, FxRequestBaseSyncCleanupContext = 0x10, }; enum FxRequestBaseStaticFlags { FxRequestBaseStaticSystemBufferValid = 0x1, FxRequestBaseStaticOutputBufferValid = 0x2, }; // // Designed to fit into a byte. FxRequestCompletionPkgFlag is a bit value // used to distinguish between calling back into the io package or the current // queue. When calling back into the current queue, we assume m_IoQueue is valid // enum FxRequestCompletionState { FxRequestCompletionStateIoPkgFlag = 0x80, FxRequestCompletionStateNone = 0x00, FxRequestCompletionStateQueue = 0x01, FxRequestCompletionStateIoPkg = 0x02 | FxRequestCompletionStateIoPkgFlag, }; class FxRequestBase : public FxNonPagedObject { friend FxIoTarget; friend FxSyncRequest; public: __inline VOID SetCompletionRoutine( __in_opt PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine = NULL, __in_opt WDFCONTEXT CompletionContext = NULL ) { m_CompletionRoutine.m_Completion = CompletionRoutine; m_TargetCompletionContext = CompletionContext; } PFN_WDF_REQUEST_COMPLETION_ROUTINE ClearCompletionRoutine( VOID ) { PFN_WDF_REQUEST_COMPLETION_ROUTINE pRoutine; pRoutine = m_CompletionRoutine.m_Completion; m_CompletionRoutine.m_Completion = NULL; return pRoutine; } WDFCONTEXT ClearCompletionContext( VOID ) { WDFCONTEXT pContext; pContext = m_TargetCompletionContext; m_TargetCompletionContext = NULL; return pContext; } __inline BOOLEAN IsCompletionRoutineSet( VOID ) { return (m_CompletionRoutine.m_Completion != NULL) ? TRUE : FALSE; } __inline BOOLEAN IsCancelRoutineSet( VOID ) { return (m_CancelRoutine.m_Cancel != NULL) ? TRUE : FALSE; } __inline FxRequestContext* GetContext( VOID ) { return m_RequestContext; } __inline VOID SetContext( __in FxRequestContext* RequestContext = NULL ) { // // If we are setting to the same value, just return // if (m_RequestContext == RequestContext) { return; } // // If we are changing the context to another unique pointer, free the // old context. // if (m_RequestContext != NULL) { delete m_RequestContext; } m_RequestContext = RequestContext; } VOID ContextReleaseAndRestore( VOID ) { // // This does not free the context, rather it tells the context to release // any references it is holding and restore fields back into the PIRP // if (m_RequestContext != NULL && m_Irp.GetIrp() != NULL) { m_RequestContext->ReleaseAndRestore(this); VerifierClearFormatted(); } } __inline VOID EnableContextDisposeNotification( VOID ) { MarkDisposeOverride(); } __inline BOOLEAN HasContextType( __in FX_REQUEST_CONTEXT_TYPE Type ) { return (m_RequestContext != NULL && m_RequestContext->m_RequestType == Type) ? TRUE : FALSE; } __inline BOOLEAN HasContext( VOID ) { return (m_RequestContext != NULL && m_RequestContext->m_RequestType != FX_REQUEST_CONTEXT_TYPE_NONE) ? TRUE : FALSE; } __inline MdIrp GetSubmitIrp( VOID ) { return m_Irp.GetIrp(); } __inline FxIrp* GetSubmitFxIrp( VOID ) { return &m_Irp; } MdIrp SetSubmitIrp( __in_opt MdIrp NewIrp, __in BOOLEAN FreeIrp = TRUE ); __inline BOOLEAN CanComplete( VOID ) { LONG count; count = InterlockedDecrement(&m_IrpCompletionReferenceCount); ASSERT(count >= 0); return count == 0 ? TRUE : FALSE; } VOID CompleteSubmitted( VOID ); _Must_inspect_result_ NTSTATUS ValidateTarget( __in FxIoTarget* Target ); __inline FxIoTarget* GetTarget( VOID ) { return m_Target; } VOID __inline SetTarget( __in FxIoTarget* Target ) { m_Target = Target; } __inline UCHAR GetTargetFlags( VOID ) { // Assumes caller is holding appropriate lock return m_TargetFlags; } __inline VOID SetTargetFlags( __in UCHAR Flags ) { // Assumes caller is holding appropriate lock m_TargetFlags |= Flags; } __inline ULONG ClearTargetFlags( __in UCHAR Flags ) { ULONG oldFlags; oldFlags = m_TargetFlags; // Assumes caller is holding appropriate lock m_TargetFlags &= ~Flags; return oldFlags; } __inline VOID SetRequestBaseFlags( __in UCHAR Flags ) { // Assumes caller is holding appropriate lock m_RequestBaseFlags|= Flags; } SHORT GetVerifierFlagsLocked( VOID ) { ASSERT(GetDriverGlobals()->FxVerifierOn); return m_VerifierFlags; } __inline SHORT GetVerifierFlags( VOID ) { SHORT flags; KIRQL irql; Lock(&irql); flags = GetVerifierFlagsLocked(); Unlock(irql); return flags; } __inline VOID SetVerifierFlagsLocked( __in SHORT Flags ) { m_VerifierFlags |= Flags; } __inline VOID SetVerifierFlags( __in SHORT Flags ) { KIRQL irql; ASSERT(GetDriverGlobals()->FxVerifierOn); Lock(&irql); SetVerifierFlagsLocked(Flags); Unlock(irql); } __inline VOID ClearVerifierFlagsLocked( __in SHORT Flags ) { m_VerifierFlags &= ~Flags; } __inline VOID ClearVerifierFlags( __in SHORT Flags ) { KIRQL irql; ASSERT(GetDriverGlobals()->FxVerifierOn); Lock(&irql); ClearVerifierFlagsLocked(Flags); Unlock(irql); } __inline VOID VerifierSetFormatted( VOID ) { if (GetDriverGlobals()->FxVerifierOn && GetDriverGlobals()->FxVerifierIO) { SetVerifierFlags(FXREQUEST_FLAG_FORMATTED); } } __inline VOID VerifierClearFormatted( VOID ) { if (GetDriverGlobals()->FxVerifierOn && GetDriverGlobals()->FxVerifierIO) { ClearVerifierFlags(FXREQUEST_FLAG_FORMATTED); } } __inline BOOLEAN VerifierIsFormatted( VOID ) { if (GetDriverGlobals()->FxVerifierOn && GetDriverGlobals()->FxVerifierIO) { return (GetVerifierFlags() & FXREQUEST_FLAG_FORMATTED) ? TRUE : FALSE; } else { // // we are not tracking the state // return TRUE; } } __inline BOOLEAN ShouldClearContext( VOID ) { return (m_RequestBaseFlags & FxRequestBaseSyncCleanupContext) ? TRUE : FALSE; } _Must_inspect_result_ NTSTATUS CreateTimer( VOID ); VOID StartTimer( __in LONGLONG Timeout ); _Must_inspect_result_ BOOLEAN CancelTimer( VOID ); BOOLEAN Cancel( VOID ); BOOLEAN __inline IsAllocatedFromIo( VOID ) { return m_IrpAllocation == REQUEST_ALLOCATED_FROM_IO ? TRUE : FALSE; } BOOLEAN __inline IsAllocatedDriver( VOID ) { return m_IrpAllocation == REQUEST_ALLOCATED_DRIVER ? TRUE : FALSE; } BOOLEAN __inline IsCanComplete( VOID ) { return m_CanComplete; } __inline VOID SetCompleted( __in BOOLEAN Value ) { m_Completed = Value; } VOID __inline SetPriorityBoost( CCHAR PriorityBoost ) { m_PriorityBoost = PriorityBoost; } CCHAR __inline GetPriorityBoost( VOID ) { return m_PriorityBoost; } __inline WDFREQUEST GetHandle( VOID ) { return (WDFREQUEST) GetObjectHandle(); } __inline static FxRequestBase* _FromListEntry( __in PLIST_ENTRY Entry ) { return CONTAINING_RECORD(Entry, FxRequestBase, m_ListEntry); } __inline static FxRequestBase* _FromDrainEntry( __in PSINGLE_LIST_ENTRY Entry ) { return CONTAINING_RECORD(Entry, FxRequestBase, m_DrainSingleEntry); } __inline static FxRequestBase* _FromCsqContext( __in PMdIoCsqIrpContext Context ) { return CONTAINING_RECORD(Context, FxRequestBase, m_CsqContext); } __inline PVOID GetTraceObjectHandle( VOID ) { PVOID handle; handle = GetObjectHandle(); if (handle != NULL) { return handle; } else { return (PVOID) this; } } VOID FreeMdls( VOID ); DECLSPEC_NORETURN VOID FatalError( __in NTSTATUS Status ); VOID ClearFieldsForReuse( VOID ); protected: FxRequestBase( __in PFX_DRIVER_GLOBALS FxDriverGlobals, __in USHORT ObjectSize, __in_opt MdIrp Irp, __in FxRequestIrpOwnership Ownership, __in FxRequestConstructorCaller Caller, __in FxObjectType ObjectType = FxObjectTypeExternal ); virtual ~FxRequestBase( VOID ); // Do not specify argument names FX_DECLARE_VF_FUNCTION( VOID, VerifyDispose ); // FxObject overrides virtual BOOLEAN Dispose( VOID ); static MdDeferredRoutineType _TimerDPC; VOID CompleteSubmittedNoContext( VOID ); VOID ZeroOutDriverContext( VOID ) { #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE) RtlZeroMemory(GetSubmitFxIrp()->GetDriverContext(), GetSubmitFxIrp()->GetDriverContextSize()); #else // // UMDF host doesn't expose any easier way to zero out the contexts so // set context to NULL one by one. // GetSubmitFxIrp()->SetContext(0, NULL); GetSubmitFxIrp()->SetContext(1, NULL); GetSubmitFxIrp()->SetContext(2, NULL); GetSubmitFxIrp()->SetContext(3, NULL); #endif } public: union { // // The cancel safe queues use this context to identify // the request in a race free manner. // MdIoCsqIrpContext m_CsqContext; // // IoTargets uses this to track the request when it is sent to the target. // Since the request cannot be on an CSQ and sent to a target at the // same time, we can unionize this with the CSQ context. // LIST_ENTRY m_ListEntry; }; union { // // IoTargest uses this when it needs to create a list of requests to cancel // when making a state transition // SINGLE_LIST_ENTRY m_DrainSingleEntry; // // If TRUE, the driver formatted the request by copying the current stack // location to next or by manually passing in an IO_STACK_LOCATION. This // is union'ed with m_DrainSingleEntry b/c it is only relevant during // send and forget and the request is never enqueued on the target in // this case and m_DrainSingleEntry is used when tracking requests sent // on a target for cancelation due to a target state change. // BOOLEAN m_NextStackLocationFormatted; }; protected: // // The NT IRP is wrapped by a frameworks FxIrp // // Note: If m_Irp is NULL after initialization, this means // the IRP was cancelled by a FxIrpQueue cancellation // callback, or completed. // FxIrp m_Irp; // // Target of the request. Access to this field is unguarded. The following // have access to this field // o The owning target itself // o _TimerDPC() // o Cancel() IFF it has successfully incremented the irp completion ref count // FxIoTarget* m_Target; FxRequestContext* m_RequestContext; FxRequestTimer* m_Timer; // // Client driver completion routine to call when the request has come back // from the target device. // FxRequestCancelCallback m_CancelRoutine; FxRequestCompletionCallback m_CompletionRoutine; // // Context to pass to CompletionRoutine when called // WDFCONTEXT m_TargetCompletionContext; // // Synchronization for this field is through Interlocked operations // LONG m_IrpCompletionReferenceCount; // // Access to flags guarded by FxIoTarget::Lock // // Values defined in the enum FxRequestTargetFlags // union { UCHAR m_TargetFlags; // // These are used purely for debugging, not in live code anywhere! // NOTE: if FxRequestTargetFlagschanges, so this this union // struct { UCHAR Completed : 1; UCHAR FlagsPended : 1; UCHAR TimerSet : 1; UCHAR CancelledFromTimer : 1; UCHAR IgnoreState : 1; } m_TargetFlagsByName; }; // // Contains a value from the enum type FxRequestAllocationSource describing // how the irp was allocated // UCHAR m_IrpAllocation; BOOLEAN m_Completed; BOOLEAN m_Canceled; WDFOBJECT_OFFSET_ALIGNED m_SystemBufferOffset; union { // // These are flags used by verifier. Set with values from the enum // FxRequestVerifierFlags // SHORT m_VerifierFlags; struct { SHORT DriverOwned : 1; SHORT TagRequest : 1; SHORT Forwarded : 1; SHORT DriverDispatch : 1; SHORT DriverCancelable : 1; SHORT DriverInprocessContext : 1; SHORT Cancelled : 1; SHORT Formatted : 1; SHORT SentToTarget : 1; SHORT DriverInEvtIoStopContext : 1; } m_VeriferFlagsByName; }; // // If this is !=0, its an indication of outstanding references // on WDM IRP fields such as any system buffers. // LONG m_IrpReferenceCount; // This field !=NULL if the request is on an IrpQueue. FxIrpQueue* m_IrpQueue; WDFOBJECT_OFFSET_ALIGNED m_OutputBufferOffset; union { // // Bit field. Set with values from the enum FxRequestBaseFlags // UCHAR m_RequestBaseFlags; struct { UCHAR SystemMdlMapped : 1; UCHAR OutputMdlMapped : 1; UCHAR SyncCleanupContext : 1; } m_RequestBaseFlagsByName; }; union { // // Bit field. Set with values from the enum FxRequestBaseStaticFlags // // UCHAR m_RequestBaseStaticFlags; struct { UCHAR SystemBufferValid : 1; UCHAR OutputBufferValid : 1; } m_RequestBaseStaticFlagsByName; }; // // Priority boost. // CCHAR m_PriorityBoost; // // Contains values from FxRequestCompletionState // BYTE m_CompletionState; // // TRUE, request can be completed by the driver. // BOOLEAN m_CanComplete; // // If !=NULL, MDL allocated and assocated with the request // PMDL m_AllocatedMdl; }; #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE)) #include "fxrequestbasekm.hpp" #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE)) #include "fxrequestbaseum.hpp" #endif #endif // _FXREQUESTBASE_H_