/*++

Copyright (c) 2000  Microsoft Corporation

Module Name:

    vfdevobj.c

Abstract:

    This module verifies drivers properly manage device objects.

Author:

    Adrian J. Oney (adriao) 09-May-1998

Environment:

    Kernel mode

Revision History:

    AdriaO      05/02/2000 - Seperated out from ntos\io\trackirp.c

--*/

//
// Disable W4 level warnings generated by public headers.
//

#include "vfpragma.h"


#include "..\io\iop.h" // Includes vfdef.h

#if (( defined(_X86_) ) && ( FPO ))
#pragma optimize( "y", off )    // disable FPO for consistent stack traces
#endif

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEVRFY, VerifierIoAttachDeviceToDeviceStack)
#pragma alloc_text(PAGEVRFY, VerifierIoDetachDevice)
#pragma alloc_text(PAGEVRFY, VerifierIoDeleteDevice)
#pragma alloc_text(PAGEVRFY, VfDevObjPreAddDevice)
#pragma alloc_text(PAGEVRFY, VfDevObjPostAddDevice)
#pragma alloc_text(PAGEVRFY, VfDevObjAdjustFdoForVerifierFilters)
#endif

VOID
VerifierIoAttachDeviceToDeviceStack(
    IN PDEVICE_OBJECT NewDevice,
    IN PDEVICE_OBJECT ExistingDevice
    )
{
    UNREFERENCED_PARAMETER (NewDevice);

    IovUtilFlushStackCache(ExistingDevice, DATABASELOCKSTATE_HELD);
}


VOID
VerifierIoDetachDevice(
    IN PDEVICE_OBJECT LowerDevice
    )
{
    PVOID callerAddress;
    ULONG stackHash;

    if (LowerDevice->AttachedDevice == NULL) {

        if (RtlCaptureStackBackTrace(2, 1, &callerAddress, &stackHash) != 1) {

            callerAddress = NULL;
        }

        WDM_FAIL_ROUTINE((
            DCERROR_DETACH_NOT_ATTACHED,
            DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
            callerAddress,
            LowerDevice
            ));
    }

    IovUtilFlushStackCache(LowerDevice, DATABASELOCKSTATE_HELD);
}


VOID
VerifierIoDeleteDevice(
    IN PDEVICE_OBJECT DeviceObject
    )
{
    PDEVICE_OBJECT deviceBelow;
    PVOID callerAddress;
    ULONG stackHash;

    if (RtlCaptureStackBackTrace(2, 1, &callerAddress, &stackHash) != 1) {

        callerAddress = NULL;
    }

    //
    // ADRIAO N.B. 06/16/2000 - A good thing to do here would be to send a
    //     second remove IRP to every deleted device object that was a member
    // of a WDM device stack. Just to check.
    //
    if (IovUtilIsDeviceObjectMarked(DeviceObject, MARKTYPE_DELETED)) {

        WDM_FAIL_ROUTINE((
            DCERROR_DOUBLE_DELETION,
            DCPARAM_ROUTINE,
            callerAddress
            ));
    }

    IovUtilMarkDeviceObject(DeviceObject, MARKTYPE_DELETED);

    IovUtilGetLowerDeviceObject(DeviceObject, &deviceBelow);
    if (deviceBelow) {

        WDM_FAIL_ROUTINE((
            DCERROR_DELETE_WHILE_ATTACHED,
            DCPARAM_ROUTINE,
            callerAddress
            ));

        ObDereferenceObject(deviceBelow);
    }
}


VOID
VfDevObjPreAddDevice(
    IN  PDEVICE_OBJECT      PhysicalDeviceObject,
    IN  PDRIVER_OBJECT      DriverObject,
    IN  PDRIVER_ADD_DEVICE  AddDeviceFunction,
    IN  VF_DEVOBJ_TYPE      DevObjType
    )
/*++

  Description:

    This routine is called before the specified driver's AddDevice has been
    invoked.

  Arguments:

     PhysicalDeviceObject - Device object at the bottom of the PnP stack.

     DriverObject - Driver object of the driver who's AddDevice has been
                    invoked.

     AddDeviceFunction - Address of the AddDevice routine.

     DevObjType - Type of device object (lower device filter, FDO, etc.)

  Return Value:

     None.

--*/
{
    VF_DEVOBJ_TYPE objType;

    UNREFERENCED_PARAMETER(AddDeviceFunction);

    if (!MmIsDriverVerifying(DriverObject)) {

        return;
    }

    if (VfSettingsIsOptionEnabled(NULL, VERIFIER_OPTION_INSERT_WDM_FILTERS)) {

        if (DevObjType == VF_DEVOBJ_FDO) {

            //
            // If we are calling AddDevice for the FDO, first attempt to attach
            // a lower class filter.
            //
            objType = VF_DEVOBJ_LOWER_CLASS_FILTER;


        } else {

            objType = DevObjType;
        }

        //
        // Attach a filter, cause pain.
        //
        VfDriverAttachFilter(PhysicalDeviceObject, objType);
    }
}


VOID
VfDevObjPostAddDevice(
    IN  PDEVICE_OBJECT      PhysicalDeviceObject,
    IN  PDRIVER_OBJECT      DriverObject,
    IN  PDRIVER_ADD_DEVICE  AddDeviceFunction,
    IN  VF_DEVOBJ_TYPE      DevObjType,
    IN  NTSTATUS            Result
    )
/*++

  Description:

    This routine is called after the specified driver's AddDevice has been
    invoked.

  Arguments:

     PhysicalDeviceObject - Device object at the bottom of the PnP stack.

     DriverObject - Driver object of the driver who's AddDevice has been
                    invoked.

     AddDeviceFunction - Address of the AddDevice routine.

     DevObjType - Type of device object (lower device filter, FDO, etc.)

     Result - Result returned by the AddDevice Routine

  Return Value:

     None.

--*/
{
    PDEVICE_OBJECT deviceAbove, deviceBelow;
    BOOLEAN powerFailure;
    VF_DEVOBJ_TYPE objType;

    UNREFERENCED_PARAMETER(DriverObject);

    if (NT_SUCCESS(Result) &&
        VfSettingsIsOptionEnabled(NULL, VERIFIER_OPTION_INSERT_WDM_FILTERS) &&
        MmIsDriverVerifying(DriverObject)) {

        if (DevObjType == VF_DEVOBJ_FDO) {

            //
            // If we've just attached an FDO, try to add a upper device filter
            // on top of it.
            //
            objType = VF_DEVOBJ_UPPER_DEVICE_FILTER;

        } else {

            objType = DevObjType;
        }

        //
        // Attach filter, cause pain.
        //
        VfDriverAttachFilter(PhysicalDeviceObject, objType);
    }

    if (!VfSettingsIsOptionEnabled(NULL, VERIFIER_OPTION_VERIFY_DO_FLAGS)) {

        return;
    }

    //
    // Take this opportunity to check the PDO.
    //
    if (!IovUtilIsDeviceObjectMarked(PhysicalDeviceObject, MARKTYPE_DEVICE_CHECKED)) {

        if ((PhysicalDeviceObject->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)) ==
            (DO_BUFFERED_IO | DO_DIRECT_IO)) {

            //
            // Both direct I/O and buffered I/O are set. These are mutually
            // exclusive.
            //
            WDM_FAIL_ROUTINE((
                DCERROR_INCONSISTANT_DO_FLAGS,
                DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                PhysicalDeviceObject->DriverObject->DriverExtension->AddDevice,
                PhysicalDeviceObject
                ));
        }

        //
        // No need to check DO_DEVICE_INITIALIZING as PDO's get them cleared
        // automagically.
        //

        IovUtilMarkDeviceObject(PhysicalDeviceObject, MARKTYPE_DEVICE_CHECKED);
    }

    powerFailure = FALSE;
    deviceBelow = PhysicalDeviceObject;
    ObReferenceObject(deviceBelow);
    while(1) {
        IovUtilGetUpperDeviceObject(deviceBelow, &deviceAbove);

        if (deviceAbove == NULL) {

            ObDereferenceObject(deviceBelow);
            break;
        }

        if (!IovUtilIsDeviceObjectMarked(deviceAbove, MARKTYPE_DEVICE_CHECKED)) {

            if ((deviceAbove->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)) ==
                (DO_BUFFERED_IO | DO_DIRECT_IO)) {

                //
                // Both direct I/O and buffered I/O are set. These are mutually
                // exclusive.
                //
                WDM_FAIL_ROUTINE((
                    DCERROR_INCONSISTANT_DO_FLAGS,
                    DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                    AddDeviceFunction,
                    deviceAbove
                    ));
            }

            if (deviceAbove->Flags & DO_DEVICE_INITIALIZING) {

                //
                // A device didn't clear the DO_DEVICE_INITIALIZING flag during
                // AddDevice. Fail it now.
                //
                WDM_FAIL_ROUTINE((
                    DCERROR_DO_INITIALIZING_NOT_CLEARED,
                    DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                    AddDeviceFunction,
                    deviceAbove
                    ));

                //
                // Clean up the mess.
                //
                deviceAbove->Flags &= ~DO_DEVICE_INITIALIZING;
            }

            if ((deviceBelow->Flags & DO_POWER_PAGABLE) &&
                (!(deviceAbove->Flags & DO_POWER_PAGABLE))) {

                if (!powerFailure) {

                    //
                    // We have caught a driver bug. deviceAbove didn't inherit the
                    // DO_POWER_PAGABLE flag.
                    //
                    WDM_FAIL_ROUTINE((
                        DCERROR_POWER_PAGABLE_NOT_INHERITED,
                        DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                        AddDeviceFunction,
                        deviceAbove
                        ));

                    //
                    // Don't blame anyone else.
                    //
                    powerFailure = TRUE;
                }

                deviceAbove->Flags |= DO_POWER_PAGABLE;
            }

            if ((deviceBelow->Flags & DO_BUFFERED_IO) &&
                (!(deviceAbove->Flags & DO_BUFFERED_IO))) {

                //
                // Buffered I/O flag not copied. Broken filter!
                //
                WDM_FAIL_ROUTINE((
                    DCERROR_DO_FLAG_NOT_COPIED,
                    DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                    AddDeviceFunction,
                    deviceAbove
                    ));
            }

            if ((deviceBelow->Flags & DO_DIRECT_IO) &&
                (!(deviceAbove->Flags & DO_DIRECT_IO))) {

                //
                // Direct I/O flag not copied. Broken filter!
                //
                WDM_FAIL_ROUTINE((
                    DCERROR_DO_FLAG_NOT_COPIED,
                    DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                    AddDeviceFunction,
                    deviceAbove
                    ));
            }

            if ((deviceBelow->DeviceType != FILE_DEVICE_UNKNOWN) &&
                (deviceAbove->DeviceType == FILE_DEVICE_UNKNOWN)) {

                //
                // The device type wasn't copied by a filter!
                //
                WDM_FAIL_ROUTINE((
                    DCERROR_DEVICE_TYPE_NOT_COPIED,
                    DCPARAM_ROUTINE + DCPARAM_DEVOBJ,
                    AddDeviceFunction,
                    deviceAbove
                    ));
            }

            //
            // Characteristics don't have to be checked because PnP takes care
            // of propogating them appropriately.
            //
        }

        IovUtilMarkDeviceObject(deviceAbove, MARKTYPE_DEVICE_CHECKED);

        ObDereferenceObject(deviceBelow);
        deviceBelow = deviceAbove;
    }
}


VOID
VfDevObjAdjustFdoForVerifierFilters(
    IN OUT  PDEVICE_OBJECT *FunctionalDeviceObject
    )
/*++

  Description:

    This routine adjusts the designated FDO to take into account any verifier
    filter DO's added by this file.

  Arguments:

     FunctionalDeviceObject - On input, contains FDO. Adjusted to point to the
                              correct FDO if verifier added a filter.

  Return Value:

     None.

--*/
{
    PDEVICE_OBJECT fdo;

    fdo = *FunctionalDeviceObject;

    if (VfDriverIsVerifierFilterObject(fdo)) {

        fdo = fdo->AttachedDevice;
        ASSERT(fdo);

        *FunctionalDeviceObject = fdo;
    }
}

