You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3108 lines
92 KiB
3108 lines
92 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1990 - 2000
|
|
|
|
Module Name:
|
|
|
|
verify.c
|
|
|
|
Abstract:
|
|
|
|
This module implements a driver verifier extension for the SCSI port
|
|
driver. Scsiport adds some of its exports to the list of apis that get
|
|
thunked by the system driver verifier, thus enabling scsiport-specific
|
|
verification of miniport drivers.
|
|
|
|
Authors:
|
|
|
|
John Strange (JohnStra)
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "port.h"
|
|
|
|
#if DBG
|
|
static const char *__file__ = __FILE__;
|
|
#endif
|
|
|
|
#define __FILE_ID__ 'vfry'
|
|
|
|
SCSIPORT_API
|
|
ULONG
|
|
ScsiPortInitializeVrfy(
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2,
|
|
IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
|
|
IN PVOID HwContext
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortCompleteRequestVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN UCHAR SrbStatus
|
|
);
|
|
|
|
SCSIPORT_API
|
|
PSCSI_REQUEST_BLOCK
|
|
ScsiPortGetSrbVrfy(
|
|
IN PVOID DeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN LONG QueueTag
|
|
);
|
|
|
|
SCSIPORT_API
|
|
PVOID
|
|
ScsiPortGetDeviceBaseVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
IN ULONG NumberOfBytes,
|
|
IN BOOLEAN InIoSpace
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortNotificationVrfy(
|
|
IN SCSI_NOTIFICATION_TYPE NotificationType,
|
|
IN PVOID HwDeviceExtension,
|
|
...
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortFlushDmaVrfy(
|
|
IN PVOID DeviceExtension
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortFreeDeviceBaseVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PVOID MappedAddress
|
|
);
|
|
|
|
SCSIPORT_API
|
|
ULONG
|
|
ScsiPortGetBusDataVrfy(
|
|
IN PVOID DeviceExtension,
|
|
IN ULONG BusDataType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length
|
|
);
|
|
|
|
SCSIPORT_API
|
|
PVOID
|
|
ScsiPortGetLogicalUnitVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun
|
|
);
|
|
|
|
SCSIPORT_API
|
|
SCSI_PHYSICAL_ADDRESS
|
|
ScsiPortGetPhysicalAddressVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID VirtualAddress,
|
|
OUT ULONG *Length
|
|
);
|
|
|
|
SCSIPORT_API
|
|
PVOID
|
|
ScsiPortGetUncachedExtensionVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
IN ULONG NumberOfBytes
|
|
);
|
|
|
|
SCSIPORT_API
|
|
PVOID
|
|
ScsiPortGetVirtualAddressVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortIoMapTransferVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID LogicalAddress,
|
|
IN ULONG Length
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortMoveMemoryVrfy(
|
|
IN PVOID WriteBuffer,
|
|
IN PVOID ReadBuffer,
|
|
IN ULONG Length
|
|
);
|
|
|
|
SCSIPORT_API
|
|
ULONG
|
|
ScsiPortSetBusDataByOffsetVrfy(
|
|
IN PVOID DeviceExtension,
|
|
IN ULONG BusDataType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
SCSIPORT_API
|
|
BOOLEAN
|
|
ScsiPortValidateRangeVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
IN ULONG NumberOfBytes,
|
|
IN BOOLEAN InIoSpace
|
|
);
|
|
|
|
SCSIPORT_API
|
|
VOID
|
|
ScsiPortStallExecutionVrfy(
|
|
IN ULONG Delay
|
|
);
|
|
|
|
PVOID
|
|
SpAllocateContiguousChunk(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDMA_ADAPTER DmaAdapterObject,
|
|
IN BOOLEAN Dma64BitAddresses,
|
|
IN ULONG Length,
|
|
IN ULONG Align,
|
|
OUT PHYSICAL_ADDRESS *PhysicalCommonBuffer,
|
|
OUT BOOLEAN *CommonBuffer
|
|
);
|
|
|
|
PVOID
|
|
SpRemapBlock(
|
|
IN PVOID BlockVa,
|
|
IN ULONG BlockSize,
|
|
OUT PMDL* Mdl
|
|
);
|
|
|
|
BOOLEAN
|
|
SpCheckForActiveRequests(
|
|
IN PADAPTER_EXTENSION
|
|
);
|
|
|
|
ULONG
|
|
SpGetAdapterVerifyLevel(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, SpVerifierInitialization)
|
|
#pragma alloc_text(PAGE, ScsiPortInitializeVrfy)
|
|
#pragma alloc_text(PAGE, SpGetAdapterVerifyLevel)
|
|
#pragma alloc_text(PAGE, SpDoVerifierInit)
|
|
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetSrbVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortCompleteRequestVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetDeviceBaseVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortNotificationVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortFlushDmaVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortFreeDeviceBaseVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetBusDataVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetLogicalUnitVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetPhysicalAddressVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetUncachedExtensionVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortGetVirtualAddressVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortMoveMemoryVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortSetBusDataByOffsetVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortValidateRangeVrfy)
|
|
#pragma alloc_text(PAGEVRFY1, ScsiPortStallExecutionVrfy)
|
|
|
|
#pragma alloc_text(PAGEVRFY, SpHwFindAdapterVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwInitializeVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwStartIoVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwInterruptVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwResetBusVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwDmaStartedVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwRequestInterruptVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwTimerRequestVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpHwAdapterControlVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpVerifySrbStatus)
|
|
#pragma alloc_text(PAGEVRFY, SpAllocateContiguousChunk)
|
|
#pragma alloc_text(PAGEVRFY, SpGetCommonBufferVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpFreeCommonBufferVrfy)
|
|
#pragma alloc_text(PAGEVRFY, SpGetOriginalSrbExtVa)
|
|
#pragma alloc_text(PAGEVRFY, SpInsertSrbExtension)
|
|
#pragma alloc_text(PAGEVRFY, SpPrepareSrbExtensionForUse)
|
|
#pragma alloc_text(PAGEVRFY, SpPrepareSenseBufferForUse)
|
|
#pragma alloc_text(PAGEVRFY, SpRemapBlock)
|
|
#pragma alloc_text(PAGEVRFY, SpGetInaccessiblePage)
|
|
#pragma alloc_text(PAGEVRFY, SpEnsureAllRequestsAreComplete)
|
|
#pragma alloc_text(PAGEVRFY, SpCheckForActiveRequests)
|
|
#endif
|
|
|
|
//
|
|
// Some defines and a macro for conditionally verifying based on the
|
|
// verification level.
|
|
//
|
|
#define SP_DONT_CHK_HW_INITIALIZE_DURATION 0x80000000
|
|
#define SP_DONT_CHK_ACTIVE_UNTAGGED_REQUEST 0x40000000
|
|
#define SP_DONT_CHK_REQUESTS_ON_RESET 0x20000000
|
|
#define SP_DONT_CHK_HW_ADAPTERCONTROL_DURATION 0x10000000
|
|
|
|
#define VRFY_DO_CHECK(adapter, chk)\
|
|
(((adapter)->VerifierExtension != NULL) &&\
|
|
(((adapter)->VerifierExtension->VrfyLevel & (chk)) == 0))
|
|
|
|
|
|
//
|
|
// Indicates whether scsiport's verifier functionality has been initialized.
|
|
//
|
|
ULONG ScsiPortVerifierInitialized = 0;
|
|
|
|
//
|
|
// Handle to pageable verifier code sections. We manually lock the verify
|
|
// code into memory iff we need it.
|
|
//
|
|
PVOID VerifierCodeSectionHandle = NULL;
|
|
PVOID VerifierApiCodeSectionHandle = NULL;
|
|
|
|
//
|
|
// Time increment of the interval timer in 100 ns units. We use this to
|
|
// calculate the time miniport routines execute so we can catch those that
|
|
// run longer than they should.
|
|
//
|
|
ULONG TimeIncrement;
|
|
|
|
//
|
|
// Global variable used to control verification aggressiveness. This value
|
|
// is used in conjuction with a per-adapter registry value, to control what
|
|
// type of verification we do on a particular miniport.
|
|
//
|
|
ULONG SpVrfyLevel = 0;
|
|
|
|
//
|
|
// Global variable used to control how aggressively we seek out stall
|
|
// offenders. Default is a tenth of a second.
|
|
//
|
|
ULONG SpVrfyMaximumStall = 100000;
|
|
|
|
//
|
|
// When verifier needs a unique address, this is what it uses.
|
|
//
|
|
ULONG SpMarker = 0x59465256;
|
|
|
|
//
|
|
// This table represents the functions verify will thunk for us.
|
|
//
|
|
#define SPVERIFIERFUNC(pfn) ((PDRIVER_VERIFIER_THUNK_ROUTINE)(pfn))
|
|
|
|
const DRIVER_VERIFIER_THUNK_PAIRS ScsiPortVerifierFunctionTable[] =
|
|
{
|
|
{SPVERIFIERFUNC(ScsiPortInitialize), SPVERIFIERFUNC(ScsiPortInitializeVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetSrb), SPVERIFIERFUNC(ScsiPortGetSrbVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortCompleteRequest), SPVERIFIERFUNC(ScsiPortCompleteRequestVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetDeviceBase), SPVERIFIERFUNC(ScsiPortGetDeviceBaseVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortNotification), SPVERIFIERFUNC(ScsiPortNotificationVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortFlushDma), SPVERIFIERFUNC(ScsiPortFlushDmaVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortFreeDeviceBase), SPVERIFIERFUNC(ScsiPortFreeDeviceBaseVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetBusData), SPVERIFIERFUNC(ScsiPortGetBusDataVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetLogicalUnit), SPVERIFIERFUNC(ScsiPortGetLogicalUnitVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetPhysicalAddress), SPVERIFIERFUNC(ScsiPortGetPhysicalAddressVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetUncachedExtension), SPVERIFIERFUNC(ScsiPortGetUncachedExtensionVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortGetVirtualAddress), SPVERIFIERFUNC(ScsiPortGetVirtualAddressVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortIoMapTransfer), SPVERIFIERFUNC(ScsiPortIoMapTransferVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortMoveMemory), SPVERIFIERFUNC(ScsiPortMoveMemoryVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortSetBusDataByOffset), SPVERIFIERFUNC(ScsiPortSetBusDataByOffsetVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortValidateRange), SPVERIFIERFUNC(ScsiPortValidateRangeVrfy)},
|
|
{SPVERIFIERFUNC(ScsiPortStallExecution), SPVERIFIERFUNC(ScsiPortStallExecutionVrfy)},
|
|
};
|
|
|
|
|
|
BOOLEAN
|
|
SpVerifierInitialization(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes scsiport's verifier functionality.
|
|
|
|
Adds several of scsiport's exported functions to the list of routines
|
|
thunked by the system verifier.
|
|
|
|
Arguments:
|
|
|
|
VOID
|
|
|
|
Return Value:
|
|
|
|
TRUE if verifier is successfully initialized.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Flags;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Query the system for verifier information. This is to ensure that
|
|
// verifier is present and operational on the system. If this fails, we
|
|
// give up and return FALSE.
|
|
//
|
|
|
|
Status = MmIsVerifierEnabled (&Flags);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Add scsiport APIs to the set that will be thunked by the system
|
|
// for verification.
|
|
//
|
|
|
|
Status = MmAddVerifierThunks ((VOID *) ScsiPortVerifierFunctionTable,
|
|
sizeof(ScsiPortVerifierFunctionTable));
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Set the system query time increment. Our verifier code uses
|
|
// this to calculate the time miniport routines take to execute.
|
|
//
|
|
|
|
TimeIncrement = KeQueryTimeIncrement();
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortInitializeVrfy(
|
|
IN PVOID Argument1,
|
|
IN PVOID Argument2,
|
|
IN struct _HW_INITIALIZATION_DATA *HwInitializationData,
|
|
IN PVOID HwContext
|
|
)
|
|
{
|
|
ULONG Result;
|
|
PDRIVER_OBJECT DriverObject = Argument1;
|
|
PSCSIPORT_DRIVER_EXTENSION DriverExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Lock the thunked API routines down.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
if (VerifierApiCodeSectionHandle == NULL) {
|
|
VerifierApiCodeSectionHandle = MmLockPagableCodeSection(ScsiPortGetSrbVrfy);
|
|
}
|
|
#endif
|
|
|
|
if (Argument1 == NULL || Argument2 == NULL) {
|
|
|
|
//
|
|
// Argument1 and Argument2 must be non-NULL.
|
|
//
|
|
|
|
KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_BAD_INIT_PARAMS,
|
|
(ULONG_PTR)Argument1,
|
|
(ULONG_PTR)Argument2,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Forward the call on to ScsiPortInitialize.
|
|
//
|
|
|
|
Result = ScsiPortInitialize (Argument1,
|
|
Argument2,
|
|
HwInitializationData,
|
|
HwContext);
|
|
|
|
//
|
|
// If initialization was successful, try to initialize verifier settings.
|
|
//
|
|
|
|
if (NT_SUCCESS(Result)) {
|
|
|
|
DriverExtension = IoGetDriverObjectExtension (DriverObject,
|
|
ScsiPortInitialize);
|
|
if (DriverExtension != NULL) {
|
|
|
|
//
|
|
// Indicate that the driver is being verified by scsiport.
|
|
//
|
|
|
|
InterlockedExchange(&DriverExtension->Verifying, 1);
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
PSCSI_REQUEST_BLOCK
|
|
ScsiPortGetSrbVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN LONG QueueTag
|
|
)
|
|
{
|
|
return ScsiPortGetSrb(HwDeviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
QueueTag);
|
|
}
|
|
|
|
VOID
|
|
ScsiPortCompleteRequestVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun,
|
|
IN UCHAR SrbStatus
|
|
)
|
|
{
|
|
ScsiPortCompleteRequest(HwDeviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
SrbStatus);
|
|
}
|
|
|
|
PVOID
|
|
ScsiPortGetDeviceBaseVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
ULONG NumberOfBytes,
|
|
BOOLEAN InIoSpace
|
|
)
|
|
{
|
|
return ScsiPortGetDeviceBase(HwDeviceExtension,
|
|
BusType,
|
|
SystemIoBusNumber,
|
|
IoAddress,
|
|
NumberOfBytes,
|
|
InIoSpace);
|
|
}
|
|
|
|
VOID
|
|
ScsiPortNotificationVrfy(
|
|
IN SCSI_NOTIFICATION_TYPE NotificationType,
|
|
IN PVOID HwDeviceExtension,
|
|
...
|
|
)
|
|
{
|
|
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
PLOGICAL_UNIT_EXTENSION logicalUnit;
|
|
PSCSI_REQUEST_BLOCK Srb;
|
|
UCHAR PathId;
|
|
UCHAR TargetId;
|
|
UCHAR Lun;
|
|
PHW_INTERRUPT HwRequestInterrupt;
|
|
PHW_INTERRUPT HwTimerRequest;
|
|
ULONG MiniportTimerValue;
|
|
PWNODE_EVENT_ITEM WnodeEventItem;
|
|
PSRB_DATA srbData;
|
|
va_list ap;
|
|
|
|
va_start(ap, HwDeviceExtension);
|
|
|
|
switch (NotificationType) {
|
|
|
|
case NextRequest:
|
|
|
|
ScsiPortNotification(NotificationType, HwDeviceExtension);
|
|
va_end(ap);
|
|
return;
|
|
|
|
case RequestComplete:
|
|
|
|
Srb = va_arg(ap, PSCSI_REQUEST_BLOCK);
|
|
|
|
//
|
|
// Check that the status makes sense.
|
|
//
|
|
|
|
SpVerifySrbStatus(HwDeviceExtension, Srb);
|
|
|
|
//
|
|
// Check that this request has not already been completed.
|
|
//
|
|
|
|
if ((Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE) == 0) {
|
|
KeBugCheckEx(
|
|
SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_REQUEST_COMPLETED_TWICE,
|
|
(ULONG_PTR)HwDeviceExtension,
|
|
(ULONG_PTR)Srb,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Restore the DataBuffer in the SRB if we plugged in our
|
|
// pointer to unmapped memory. We did this if the adapter
|
|
// does not specify MappedBuffers because the miniport is
|
|
// not supposed to touch the buffer in that case.
|
|
//
|
|
|
|
srbData = (PSRB_DATA)Srb->OriginalRequest;
|
|
ASSERT_SRB_DATA(srbData);
|
|
|
|
if (srbData->UnmappedDataBuffer != &SpMarker) {
|
|
ASSERT(srbData->UnmappedDataBuffer != NULL);
|
|
Srb->DataBuffer = srbData->UnmappedDataBuffer;
|
|
}
|
|
srbData->UnmappedDataBuffer = NULL;
|
|
|
|
//
|
|
// Forward on to the real ScsiPortNotification routine.
|
|
//
|
|
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
Srb);
|
|
|
|
va_end(ap);
|
|
return;
|
|
|
|
case ResetDetected:
|
|
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension);
|
|
va_end(ap);
|
|
return;
|
|
|
|
case NextLuRequest:
|
|
|
|
//
|
|
// The miniport driver is ready for the next request and
|
|
// can accept a request for this logical unit.
|
|
//
|
|
|
|
PathId = va_arg(ap, UCHAR);
|
|
TargetId = va_arg(ap, UCHAR);
|
|
Lun = va_arg(ap, UCHAR);
|
|
|
|
logicalUnit = deviceExtension->CachedLogicalUnit;
|
|
|
|
if ((logicalUnit == NULL)
|
|
|| (logicalUnit->TargetId != TargetId)
|
|
|| (logicalUnit->PathId != PathId)
|
|
|| (logicalUnit->Lun != Lun)) {
|
|
|
|
logicalUnit = GetLogicalUnitExtension(deviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun,
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
|
|
//
|
|
// Bugcheck if there is an untagged request active for this
|
|
// logical unit.
|
|
//
|
|
|
|
if (VRFY_DO_CHECK(deviceExtension, SP_DONT_CHK_ACTIVE_UNTAGGED_REQUEST) &&
|
|
logicalUnit != NULL &&
|
|
logicalUnit->CurrentUntaggedRequest != NULL &&
|
|
logicalUnit->CurrentUntaggedRequest->CurrentSrb != NULL &&
|
|
logicalUnit->CurrentUntaggedRequest->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
|
|
|
|
KeBugCheckEx (
|
|
SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_UNTAGGED_REQUEST_ACTIVE,
|
|
(ULONG_PTR)HwDeviceExtension,
|
|
(ULONG_PTR)logicalUnit,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Forward on to the real ScsiPortNotification.
|
|
//
|
|
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun);
|
|
va_end(ap);
|
|
return;
|
|
|
|
case CallDisableInterrupts:
|
|
|
|
HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
HwRequestInterrupt);
|
|
va_end(ap);
|
|
return;
|
|
|
|
case CallEnableInterrupts:
|
|
|
|
HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
HwRequestInterrupt);
|
|
va_end(ap);
|
|
return;
|
|
|
|
case RequestTimerCall:
|
|
|
|
HwTimerRequest = va_arg(ap, PHW_INTERRUPT);
|
|
MiniportTimerValue = va_arg(ap, ULONG);
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
HwTimerRequest,
|
|
MiniportTimerValue);
|
|
va_end(ap);
|
|
return;
|
|
|
|
case WMIEvent:
|
|
|
|
WnodeEventItem = va_arg(ap, PWNODE_EVENT_ITEM);
|
|
PathId = va_arg(ap, UCHAR);
|
|
|
|
if (PathId != 0xFF) {
|
|
TargetId = va_arg(ap, UCHAR);
|
|
Lun = va_arg(ap, UCHAR);
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
WnodeEventItem,
|
|
PathId,
|
|
TargetId,
|
|
Lun);
|
|
} else {
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
WnodeEventItem,
|
|
PathId);
|
|
}
|
|
va_end(ap);
|
|
return;
|
|
|
|
case WMIReregister:
|
|
|
|
PathId = va_arg(ap, UCHAR);
|
|
|
|
if (PathId != 0xFF) {
|
|
TargetId = va_arg(ap, UCHAR);
|
|
Lun = va_arg(ap, UCHAR);
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun);
|
|
} else {
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension,
|
|
PathId);
|
|
}
|
|
va_end(ap);
|
|
return;
|
|
|
|
case BusChangeDetected:
|
|
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension);
|
|
va_end(ap);
|
|
return;
|
|
|
|
default:
|
|
|
|
ScsiPortNotification(NotificationType,
|
|
HwDeviceExtension);
|
|
va_end(ap);
|
|
return;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
ScsiPortFlushDmaVrfy(
|
|
IN PVOID DeviceExtension
|
|
)
|
|
{
|
|
ScsiPortFlushDma(DeviceExtension);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ScsiPortFreeDeviceBaseVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PVOID MappedAddress
|
|
)
|
|
{
|
|
ScsiPortFreeDeviceBase(HwDeviceExtension, MappedAddress);
|
|
return;
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortGetBusDataVrfy(
|
|
IN PVOID DeviceExtension,
|
|
IN ULONG BusDataType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
ULONG BusData;
|
|
|
|
if (BusDataType != PCIConfiguration) {
|
|
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_BAD_BUSDATATYPE,
|
|
(ULONG_PTR)BusDataType,
|
|
(ULONG_PTR)DeviceExtension,
|
|
(ULONG_PTR)SystemIoBusNumber);
|
|
}
|
|
|
|
BusData = ScsiPortGetBusData(DeviceExtension,
|
|
BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
Buffer,
|
|
Length);
|
|
return BusData;
|
|
}
|
|
|
|
PVOID
|
|
ScsiPortGetLogicalUnitVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN UCHAR PathId,
|
|
IN UCHAR TargetId,
|
|
IN UCHAR Lun
|
|
)
|
|
{
|
|
PVOID LogicalUnit;
|
|
LogicalUnit = ScsiPortGetLogicalUnit(HwDeviceExtension,
|
|
PathId,
|
|
TargetId,
|
|
Lun);
|
|
return LogicalUnit;
|
|
}
|
|
|
|
SCSI_PHYSICAL_ADDRESS
|
|
ScsiPortGetPhysicalAddressVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID VirtualAddress,
|
|
OUT ULONG *Length
|
|
)
|
|
{
|
|
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
PHYSICAL_ADDRESS address;
|
|
PSP_VA_MAPPING_INFO MappingInfo;
|
|
ULONG byteOffset;
|
|
ULONG length;
|
|
|
|
if ((deviceExtension->VerifierExtension != NULL) &&
|
|
(deviceExtension->VerifierExtension->VrfyLevel & SP_VRFY_COMMON_BUFFERS) &&
|
|
(Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress)) {
|
|
|
|
ULONG i;
|
|
PVOID* BlkAddr;
|
|
PUCHAR Beginning, End;
|
|
PHYSICAL_ADDRESS *AddressBlock;
|
|
|
|
//
|
|
// Initialize a pointer to our array of common memory block
|
|
// descriptors. We use this to locate the block that contains
|
|
// the VA.
|
|
//
|
|
|
|
BlkAddr = deviceExtension->VerifierExtension->CommonBufferVAs;
|
|
|
|
//
|
|
// Look for the block that contains the VA.
|
|
//
|
|
|
|
for (i = 0; i < deviceExtension->NumberOfRequests; i++) {
|
|
|
|
//
|
|
// First, check if the VA is in the SRB extension.
|
|
//
|
|
|
|
MappingInfo = GET_VA_MAPPING_INFO(deviceExtension, BlkAddr[i]);
|
|
if (MappingInfo->RemappedSrbExtVa != NULL) {
|
|
Beginning = MappingInfo->RemappedSrbExtVa;
|
|
} else {
|
|
Beginning = BlkAddr[i];
|
|
}
|
|
End = (PUCHAR)ROUND_TO_PAGES((PUCHAR)Beginning + deviceExtension->SrbExtensionSize);
|
|
|
|
if ((PUCHAR)VirtualAddress >= Beginning &&
|
|
(PUCHAR)VirtualAddress < End) {
|
|
byteOffset = (ULONG)((PUCHAR)VirtualAddress - Beginning);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Next, check if the VA is in the Sense Data Buffer.
|
|
//
|
|
|
|
if (deviceExtension->AutoRequestSense == TRUE) {
|
|
|
|
if (MappingInfo->RemappedSenseVa != NULL) {
|
|
Beginning = MappingInfo->RemappedSenseVa;
|
|
} else {
|
|
Beginning = (PUCHAR)BlkAddr[i] +
|
|
ROUND_TO_PAGES(deviceExtension->SrbExtensionSize);
|
|
}
|
|
End = Beginning + PAGE_SIZE;
|
|
|
|
if ((PUCHAR)VirtualAddress >= Beginning &&
|
|
(PUCHAR)VirtualAddress < End) {
|
|
byteOffset = (ULONG)((PUCHAR)VirtualAddress - Beginning) +
|
|
(ULONG)ROUND_TO_PAGES(deviceExtension->SrbExtensionSize);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we haven't found the VA yet, it must be in the non-cached
|
|
// extension.
|
|
//
|
|
|
|
if (i == deviceExtension->NumberOfRequests) {
|
|
|
|
if (deviceExtension->VerifierExtension->NonCachedBufferSize != 0) {
|
|
|
|
Beginning = BlkAddr[i];
|
|
End = (PUCHAR) ROUND_TO_PAGES(
|
|
(PUCHAR)Beginning +
|
|
deviceExtension->VerifierExtension->NonCachedBufferSize);
|
|
|
|
if ((PUCHAR)VirtualAddress < Beginning &&
|
|
(PUCHAR)VirtualAddress >= End) {
|
|
|
|
KeBugCheckEx (SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_BAD_VA,
|
|
(ULONG_PTR)HwDeviceExtension,
|
|
(ULONG_PTR)VirtualAddress,
|
|
0);
|
|
}
|
|
|
|
byteOffset = (ULONG)((PUCHAR)VirtualAddress - Beginning);
|
|
|
|
} else {
|
|
|
|
KeBugCheckEx (SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_BAD_VA,
|
|
(ULONG_PTR)HwDeviceExtension,
|
|
(ULONG_PTR)VirtualAddress,
|
|
0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the physical address.
|
|
//
|
|
|
|
AddressBlock = deviceExtension->VerifierExtension->CommonBufferPAs;
|
|
address.QuadPart = AddressBlock[i].QuadPart + byteOffset;
|
|
|
|
//
|
|
// Calculate the length of the block.
|
|
//
|
|
|
|
length = (ULONG)((End - (PUCHAR)VirtualAddress) + 1);
|
|
|
|
return address;
|
|
}
|
|
|
|
//
|
|
// Forward on to the real routine.
|
|
//
|
|
|
|
address = ScsiPortGetPhysicalAddress(HwDeviceExtension,
|
|
Srb,
|
|
VirtualAddress,
|
|
Length);
|
|
return address;
|
|
}
|
|
|
|
PVOID
|
|
ScsiPortGetUncachedExtensionVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
IN ULONG NumberOfBytes
|
|
)
|
|
{
|
|
PVOID Extension;
|
|
Extension = ScsiPortGetUncachedExtension(HwDeviceExtension,
|
|
ConfigInfo,
|
|
NumberOfBytes);
|
|
return Extension;
|
|
}
|
|
|
|
|
|
PVOID
|
|
ScsiPortGetVirtualAddressVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
|
|
)
|
|
{
|
|
PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
PVOID* BlkAddr;
|
|
PSP_VA_MAPPING_INFO MappingInfo;
|
|
ULONG smallphysicalBase;
|
|
ULONG smallAddress;
|
|
PVOID address;
|
|
ULONG offset;
|
|
ULONG Size;
|
|
ULONG i;
|
|
|
|
//
|
|
// If the adapter is not configured to allocate multiple common buffer
|
|
// blocks during verification, just call the scsiport routine.
|
|
//
|
|
|
|
if ((deviceExtension->VerifierExtension == NULL) ||
|
|
(deviceExtension->VerifierExtension->VrfyLevel & SP_VRFY_COMMON_BUFFERS) == 0) {
|
|
return ScsiPortGetVirtualAddress(HwDeviceExtension, PhysicalAddress);
|
|
}
|
|
|
|
BlkAddr = deviceExtension->VerifierExtension->CommonBufferVAs;
|
|
|
|
//
|
|
// Convert the 64-bit physical address to a ULONG.
|
|
//
|
|
|
|
smallAddress = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress);
|
|
|
|
//
|
|
// Check first if the supplied physical address is in an SRB extension or
|
|
// in a sense buffer.
|
|
//
|
|
|
|
for (i = 0; i < deviceExtension->NumberOfRequests; i++) {
|
|
|
|
smallphysicalBase =
|
|
ScsiPortConvertPhysicalAddressToUlong(
|
|
deviceExtension->VerifierExtension->CommonBufferPAs[i]);
|
|
|
|
if ((smallAddress < smallphysicalBase) ||
|
|
(smallAddress >= smallphysicalBase +
|
|
(deviceExtension->CommonBufferSize -
|
|
PAGE_SIZE))) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Calculate the address of the buffer.
|
|
//
|
|
|
|
offset = smallAddress - smallphysicalBase;
|
|
address = offset + (PUCHAR)BlkAddr[i];
|
|
|
|
MappingInfo = GET_VA_MAPPING_INFO(deviceExtension, BlkAddr[i]);
|
|
|
|
goto GotAddress;
|
|
}
|
|
|
|
//
|
|
// Check if the supplied physical address is in the non-cached extension.
|
|
//
|
|
|
|
if (deviceExtension->VerifierExtension->NonCachedBufferSize == 0) {
|
|
|
|
ASSERT(FALSE);
|
|
return(NULL);
|
|
|
|
} else {
|
|
|
|
smallphysicalBase =
|
|
ScsiPortConvertPhysicalAddressToUlong(
|
|
deviceExtension->VerifierExtension->CommonBufferPAs[i]);
|
|
|
|
if ((smallAddress < smallphysicalBase) ||
|
|
(smallAddress >= smallphysicalBase +
|
|
deviceExtension->VerifierExtension->NonCachedBufferSize)) {
|
|
|
|
//
|
|
// This is a bogus physical address return back NULL.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
return(NULL);
|
|
}
|
|
|
|
offset = smallAddress - smallphysicalBase;
|
|
address = offset + (PUCHAR)BlkAddr[i];
|
|
|
|
Size = (ULONG)ROUND_TO_PAGES(deviceExtension->VerifierExtension->NonCachedBufferSize);
|
|
MappingInfo = (PSP_VA_MAPPING_INFO)((PUCHAR)BlkAddr[i] + (Size - PAGE_SIZE));
|
|
|
|
}
|
|
|
|
GotAddress:
|
|
//
|
|
// Find out if we've remapped this address. If we have, give the
|
|
// caller the second mapping.
|
|
//
|
|
|
|
if (address < MappingInfo->OriginalSenseVa &&
|
|
MappingInfo->RemappedSrbExtVa != NULL) {
|
|
return(offset + (PUCHAR)MappingInfo->RemappedSrbExtVa);
|
|
}
|
|
else if (MappingInfo->RemappedSenseVa != NULL) {
|
|
return(offset + (PUCHAR)MappingInfo->RemappedSenseVa);
|
|
}
|
|
|
|
return(address);
|
|
}
|
|
|
|
VOID
|
|
ScsiPortIoMapTransferVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb,
|
|
IN PVOID LogicalAddress,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
ScsiPortIoMapTransfer(HwDeviceExtension,
|
|
Srb,
|
|
LogicalAddress,
|
|
Length);
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ScsiPortMoveMemoryVrfy(
|
|
IN PVOID WriteBuffer,
|
|
IN PVOID ReadBuffer,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
ScsiPortMoveMemory(WriteBuffer,
|
|
ReadBuffer,
|
|
Length);
|
|
return;
|
|
}
|
|
|
|
ULONG
|
|
ScsiPortSetBusDataByOffsetVrfy(
|
|
IN PVOID DeviceExtension,
|
|
IN ULONG BusDataType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
{
|
|
ULONG Result;
|
|
|
|
if (BusDataType != PCIConfiguration) {
|
|
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_BAD_BUSDATATYPE,
|
|
(ULONG_PTR)BusDataType,
|
|
(ULONG_PTR)DeviceExtension,
|
|
(ULONG_PTR)SystemIoBusNumber);
|
|
}
|
|
|
|
Result = ScsiPortSetBusDataByOffset(DeviceExtension,
|
|
BusDataType,
|
|
SystemIoBusNumber,
|
|
SlotNumber,
|
|
Buffer,
|
|
Offset,
|
|
Length);
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
ScsiPortValidateRangeVrfy(
|
|
IN PVOID HwDeviceExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG SystemIoBusNumber,
|
|
IN SCSI_PHYSICAL_ADDRESS IoAddress,
|
|
IN ULONG NumberOfBytes,
|
|
IN BOOLEAN InIoSpace
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
Result = ScsiPortValidateRange(HwDeviceExtension,
|
|
BusType,
|
|
SystemIoBusNumber,
|
|
IoAddress,
|
|
NumberOfBytes,
|
|
InIoSpace);
|
|
return Result;
|
|
}
|
|
|
|
VOID
|
|
ScsiPortStallExecutionVrfy(
|
|
IN ULONG Delay
|
|
)
|
|
{
|
|
//
|
|
// Miniports must specify a delay not more than one millisecond.
|
|
//
|
|
|
|
if (Delay > SpVrfyMaximumStall) {
|
|
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_STALL_TOO_LONG,
|
|
(ULONG_PTR)Delay,
|
|
0,
|
|
0);
|
|
}
|
|
|
|
KeStallExecutionProcessor(Delay);
|
|
}
|
|
|
|
//
|
|
// Timeout periods in ticks. To calculate, we divide the time limit in 100 ns
|
|
// units by the TimeIncrement, which is the value returned by
|
|
// KeQueryTimeIncrement. Since KeQueryTickCount rounds up to the next
|
|
// tick, we'll add one tick to the defined limits.
|
|
//
|
|
#define SP_FIVE_SECOND_LIMIT ((50000000L / TimeIncrement) + 1)
|
|
#define SP_TWO_SECOND_LIMIT ((20000000L / TimeIncrement) + 1)
|
|
#define SP_HALF_SECOND_LIMIT ((5000000L / TimeIncrement) + 1)
|
|
|
|
/*++
|
|
|
|
Macro Description:
|
|
|
|
This macro checks the number of ticks elapsed during the execution of a
|
|
miniport routine against a maximum number allowed ticks. If the routine
|
|
ran longer than the max allowable ticks, we bugcheck.
|
|
|
|
Arguments:
|
|
|
|
Ticks - number of ticks routine took to execute.
|
|
|
|
MaxTicks - number of ticks the routine is allowed to execute.
|
|
|
|
Routine - address of the routine we are checking.
|
|
|
|
Extension - address of the miniport's HwDeviceExtension
|
|
|
|
Notes:
|
|
|
|
The format for the bugcheck is:
|
|
Parameter 1: 0x1002
|
|
Parameter 2: address of routine that ran too long
|
|
Parameter 3: address of miniport's HwDeviceExtension
|
|
Parameter 4: duration of routine in microseconds
|
|
|
|
--*/
|
|
/*
|
|
#define SpCheckMiniportRoutineDuration(Ticks, MaxTicks, Routine, Extension) \
|
|
{ \
|
|
if ((Ticks) > (MaxTicks)) { \
|
|
KeBugCheckEx ( \
|
|
SCSI_VERIFIER_DETECTED_VIOLATION, \
|
|
SCSIPORT_VERIFIER_MINIPORT_ROUTINE_TIMEOUT, \
|
|
(ULONG_PTR)(Routine), \
|
|
(ULONG_PTR)(Extension), \
|
|
(ULONG_PTR)(((Ticks) * TimeIncrement) / 10)); \
|
|
} \
|
|
}
|
|
*/
|
|
#define SpCheckMiniportRoutineDuration(Ticks, MaxTicks, Routine, Extension)
|
|
|
|
ULONG
|
|
SpHwFindAdapterVrfy (
|
|
IN PVOID HwDeviceExtension,
|
|
IN PVOID HwContext,
|
|
IN PVOID BusInformation,
|
|
IN PCHAR ArgumentString,
|
|
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
|
|
OUT PBOOLEAN Again
|
|
)
|
|
{
|
|
ULONG Result;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
Result = AdapterExtension->VerifierExtension->RealHwFindAdapter(
|
|
HwDeviceExtension,
|
|
HwContext,
|
|
BusInformation,
|
|
ArgumentString,
|
|
ConfigInfo,
|
|
Again);
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpHwInitializeVrfy (
|
|
IN PVOID HwDeviceExtension
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
KeQueryTickCount(&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwInitialize(HwDeviceExtension);
|
|
KeQueryTickCount(&Duration);
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
|
|
if (VRFY_DO_CHECK(AdapterExtension, SP_DONT_CHK_HW_INITIALIZE_DURATION)) {
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_FIVE_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwInitialize,
|
|
HwDeviceExtension);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpHwStartIoVrfy (
|
|
IN PVOID HwDeviceExtension,
|
|
IN PSCSI_REQUEST_BLOCK Srb
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
PSRB_DATA srbData;
|
|
|
|
//
|
|
// If MapBuffers is not set, the miniport is not supposed to touch
|
|
// the DataBuffer field in the SRB. To verify this, we'll set
|
|
// DataBuffer to point to memory that will fault if the miniport tries
|
|
// to touch it.
|
|
//
|
|
|
|
ASSERT(Srb != NULL);
|
|
srbData = (PSRB_DATA)Srb->OriginalRequest;
|
|
ASSERT_SRB_DATA(srbData);
|
|
|
|
if (AdapterExtension->MapBuffers == FALSE
|
|
&& !IS_MAPPED_SRB(Srb)
|
|
&& Srb->Function != SRB_FUNCTION_WMI
|
|
&& Srb->DataBuffer != NULL
|
|
&& AdapterExtension->VerifierExtension->InvalidPage != NULL
|
|
&& Srb->Cdb[0] != SCSIOP_INQUIRY
|
|
&& Srb->Cdb[0] != SCSIOP_REPORT_LUNS) {
|
|
|
|
if (Srb->DataBuffer != AdapterExtension->VerifierExtension->InvalidPage) {
|
|
srbData->UnmappedDataBuffer = Srb->DataBuffer;
|
|
Srb->DataBuffer = AdapterExtension->VerifierExtension->InvalidPage;
|
|
} else {
|
|
ASSERT(srbData->UnmappedDataBuffer != &SpMarker);
|
|
ASSERT(srbData->UnmappedDataBuffer != NULL);
|
|
}
|
|
|
|
} else {
|
|
|
|
srbData->UnmappedDataBuffer = &SpMarker;
|
|
|
|
}
|
|
|
|
//
|
|
// Call the miniport's StartIo function and calculate the call's duration.
|
|
//
|
|
|
|
KeQueryTickCount(&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwStartIo(
|
|
HwDeviceExtension,
|
|
Srb);
|
|
KeQueryTickCount(&Duration);
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
|
|
//
|
|
// Bugcheck if the call took more than .5 seconds.
|
|
//
|
|
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwStartIo,
|
|
HwDeviceExtension);
|
|
|
|
//
|
|
// If the HwStartIo returns failure, undo any fixups we performed on the SRB.
|
|
//
|
|
|
|
if (Result == FALSE
|
|
&& srbData->UnmappedDataBuffer != &SpMarker) {
|
|
|
|
ASSERT(srbData->UnmappedDataBuffer != NULL);
|
|
Srb->DataBuffer = srbData->UnmappedDataBuffer;
|
|
srbData->UnmappedDataBuffer = NULL;
|
|
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpHwInterruptVrfy (
|
|
IN PVOID HwDeviceExtension
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
if (AdapterExtension->VerifierExtension->RealHwInterrupt == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
KeQueryTickCount(&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwInterrupt (
|
|
HwDeviceExtension);
|
|
KeQueryTickCount(&Duration);
|
|
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwInterrupt,
|
|
HwDeviceExtension);
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpHwResetBusVrfy (
|
|
IN PVOID HwDeviceExtension,
|
|
IN ULONG PathId
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
KeQueryTickCount(&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwResetBus(
|
|
HwDeviceExtension,
|
|
PathId);
|
|
KeQueryTickCount(&Duration);
|
|
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwResetBus,
|
|
HwDeviceExtension);
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID
|
|
SpHwDmaStartedVrfy (
|
|
IN PVOID HwDeviceExtension
|
|
)
|
|
{
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
KeQueryTickCount(&Start);
|
|
AdapterExtension->VerifierExtension->RealHwDmaStarted(
|
|
HwDeviceExtension);
|
|
KeQueryTickCount(&Duration);
|
|
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwDmaStarted,
|
|
HwDeviceExtension);
|
|
}
|
|
|
|
BOOLEAN
|
|
SpHwRequestInterruptVrfy (
|
|
IN PVOID HwDeviceExtension
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
KeQueryTickCount(&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwRequestInterrupt(
|
|
HwDeviceExtension);
|
|
KeQueryTickCount(&Duration);
|
|
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwRequestInterrupt,
|
|
HwDeviceExtension);
|
|
|
|
return Result;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpHwTimerRequestVrfy (
|
|
IN PVOID HwDeviceExtension
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
KeQueryTickCount(&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwTimerRequest(
|
|
HwDeviceExtension);
|
|
KeQueryTickCount(&Duration);
|
|
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwTimerRequest,
|
|
HwDeviceExtension);
|
|
|
|
return Result;
|
|
}
|
|
|
|
SCSI_ADAPTER_CONTROL_STATUS
|
|
SpHwAdapterControlVrfy (
|
|
IN PVOID HwDeviceExtension,
|
|
IN SCSI_ADAPTER_CONTROL_TYPE ControlType,
|
|
IN PVOID Parameters
|
|
)
|
|
{
|
|
SCSI_ADAPTER_CONTROL_STATUS Result;
|
|
LARGE_INTEGER Start;
|
|
LARGE_INTEGER Duration;
|
|
PADAPTER_EXTENSION AdapterExtension = GET_FDO_EXTENSION(HwDeviceExtension);
|
|
|
|
KeQueryTickCount (&Start);
|
|
Result = AdapterExtension->VerifierExtension->RealHwAdapterControl(
|
|
HwDeviceExtension,
|
|
ControlType,
|
|
Parameters);
|
|
KeQueryTickCount(&Duration);
|
|
Duration.QuadPart -= Start.QuadPart;
|
|
|
|
if (VRFY_DO_CHECK(AdapterExtension, SP_DONT_CHK_HW_ADAPTERCONTROL_DURATION)) {
|
|
SpCheckMiniportRoutineDuration(
|
|
Duration.LowPart,
|
|
SP_HALF_SECOND_LIMIT,
|
|
AdapterExtension->VerifierExtension->RealHwAdapterControl,
|
|
HwDeviceExtension);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
VOID
|
|
SpVerifySrbStatus(
|
|
PVOID HwDeviceExtension,
|
|
PSCSI_REQUEST_BLOCK srb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify that the SRB's status as set by the miniport driver is valid.
|
|
|
|
Arguments:
|
|
|
|
HwDeviceExtension - The port driver's device extension follows the
|
|
miniport's device extension and contains a pointer to
|
|
the logical device extension list.
|
|
|
|
srb - Points to the SRB.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR SrbStatus;
|
|
|
|
//
|
|
// Turn off internal bits used by scsiport.
|
|
//
|
|
|
|
SrbStatus = srb->SrbStatus & ~(SRB_STATUS_QUEUE_FROZEN |
|
|
SRB_STATUS_AUTOSENSE_VALID);
|
|
|
|
//
|
|
// Miniports may never set the status to SRB_STATUS_PENDING.
|
|
//
|
|
|
|
if (SrbStatus == SRB_STATUS_PENDING) {
|
|
goto BadStatus;
|
|
}
|
|
|
|
//
|
|
// If the function is SRB_FUNCTION_EXECUTE_SCSI, then the command must be
|
|
// either completed successfully, or ScsiStatus must be set to
|
|
// SCSISTAT_GOOD.
|
|
//
|
|
|
|
if (!(SrbStatus != SRB_STATUS_SUCCESS ||
|
|
srb->ScsiStatus == SCSISTAT_GOOD ||
|
|
srb->Function != SRB_FUNCTION_EXECUTE_SCSI)) {
|
|
goto BadStatus;
|
|
}
|
|
|
|
//
|
|
// Make sure the status is within the valid range.
|
|
//
|
|
|
|
if ((SrbStatus) == 0x0C ||
|
|
(SrbStatus > 0x16 && srb->SrbStatus < 0x20) ||
|
|
(SrbStatus > 0x23)) {
|
|
goto BadStatus;
|
|
}
|
|
|
|
//
|
|
// The SRB Status is ok.
|
|
//
|
|
|
|
return;
|
|
|
|
BadStatus:
|
|
//
|
|
// Bugcheck if the status is bad.
|
|
//
|
|
|
|
KeBugCheckEx (SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_BAD_SRBSTATUS,
|
|
(ULONG_PTR)srb,
|
|
(ULONG_PTR)HwDeviceExtension,
|
|
0);
|
|
}
|
|
|
|
PVOID
|
|
SpRemapBlock(
|
|
IN PVOID BlockVa,
|
|
IN ULONG BlockSize,
|
|
OUT PMDL* Mdl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to remap the supplied VA range. If the block is
|
|
remapped, it will be made invalid for reading and writing.
|
|
|
|
Arguments:
|
|
|
|
BlockVa - Supplies the address of the block of memory to remap.
|
|
|
|
BlockSize - Supplies the size of the block of memory to remap.
|
|
|
|
Mdl - Supplies the address into which the function will store
|
|
a pointer to the MDL for the remapped range. If the MDL
|
|
cannot be allocated or if the range cannot be remapped,
|
|
this will be NULL upon return.
|
|
|
|
Return Value:
|
|
|
|
If the range is successfully remapped, the address of the beginning of
|
|
the remapped range is returned. Else, NULL is returned.
|
|
|
|
--*/
|
|
{
|
|
PVOID MappedRange;
|
|
NTSTATUS Status;
|
|
PMDL LocalMdl;
|
|
|
|
//
|
|
// Try to allocate a new MDL for the range we're trying to remap.
|
|
//
|
|
|
|
LocalMdl = IoAllocateMdl(BlockVa, BlockSize, FALSE, FALSE, NULL);
|
|
if (LocalMdl == NULL) {
|
|
*Mdl = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Try to lock the pages. This initializes the MDL properly.
|
|
//
|
|
|
|
__try {
|
|
MmProbeAndLockPages(LocalMdl, KernelMode, IoModifyAccess);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
IoFreeMdl(LocalMdl);
|
|
*Mdl = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Try to remap the range represented by the new MDL.
|
|
//
|
|
|
|
MappedRange = MmMapLockedPagesSpecifyCache(LocalMdl,
|
|
KernelMode,
|
|
MmCached,
|
|
NULL,
|
|
FALSE,
|
|
NormalPagePriority);
|
|
if (MappedRange == NULL) {
|
|
IoFreeMdl(LocalMdl);
|
|
*Mdl = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// If we've gotten this far, we have successfully remapped the range.
|
|
// Now we want to invalidate the entire range so any accesses to it
|
|
// will be trapped by the system.
|
|
//
|
|
|
|
Status = MmProtectMdlSystemAddress(LocalMdl, PAGE_NOACCESS);
|
|
#if DBG==1
|
|
if (!NT_SUCCESS(Status)) {
|
|
DebugPrint((0, "SpRemapBlock: failed to remap block:%p mdl:%p (%x)\n",
|
|
BlockVa, LocalMdl, Status));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Copy the MDL we allocated into the supplied address and return the
|
|
// address of the beginning of the remapped range.
|
|
//
|
|
|
|
*Mdl = LocalMdl;
|
|
return MappedRange;
|
|
}
|
|
|
|
VOID
|
|
SpRemapCommonBufferForMiniport(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine attempts to remap all of the common buffer blocks allocated
|
|
for a particular adapter.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the adapter device extension.
|
|
|
|
--*/
|
|
{
|
|
PVOID* BlkAddr = Adapter->VerifierExtension->CommonBufferVAs;
|
|
PSP_VA_MAPPING_INFO MappingInfo;
|
|
PVOID RemappedVa;
|
|
ULONG Size;
|
|
PMDL Mdl;
|
|
ULONG i;
|
|
|
|
//
|
|
// Iterate through all of the common buffer blocks, and attempt to remap
|
|
// the SRB extension and the sense buffer within each block.
|
|
//
|
|
|
|
for (i = 0; i < Adapter->VerifierExtension->CommonBufferBlocks; i++) {
|
|
|
|
//
|
|
// Get a pointer to the mapping info we keep at the end of the block.
|
|
//
|
|
|
|
MappingInfo = GET_VA_MAPPING_INFO(Adapter, BlkAddr[i]);
|
|
|
|
//
|
|
// Initialize the original VA info for the SRB extension.
|
|
//
|
|
|
|
MappingInfo->OriginalSrbExtVa = BlkAddr[i];
|
|
MappingInfo->SrbExtLen = (ULONG)ROUND_TO_PAGES(Adapter->SrbExtensionSize);
|
|
|
|
//
|
|
// Initialize the original VA info for the sense buffer.
|
|
//
|
|
|
|
MappingInfo->OriginalSenseVa = (PUCHAR)BlkAddr[i] + MappingInfo->SrbExtLen;
|
|
MappingInfo->SenseLen = PAGE_SIZE;
|
|
|
|
//
|
|
// Try to remap the SRB extension. If successful, initialize the
|
|
// remapped VA info for the SRB extension.
|
|
//
|
|
|
|
RemappedVa = SpRemapBlock(MappingInfo->OriginalSrbExtVa,
|
|
MappingInfo->SrbExtLen,
|
|
&Mdl);
|
|
if (RemappedVa != NULL) {
|
|
MappingInfo->RemappedSrbExtVa = RemappedVa;
|
|
MappingInfo->SrbExtMdl = Mdl;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// Try to remap the sense buffer. If successful, initialize the
|
|
// remapped VA info for the sense buffer.
|
|
//
|
|
// For now, I think we can live without this. I don't know of any
|
|
// issues where overruns etc. occur in a sense buffer.
|
|
//
|
|
|
|
RemappedVa = SpRemapBlock(MappingInfo->OriginalSenseVa,
|
|
MappingInfo->SenseLen,
|
|
&Mdl);
|
|
if (RemappedVa != NULL) {
|
|
MappingInfo->RemappedSenseVa = RemappedVa;
|
|
MappingInfo->SenseMdl = Mdl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (Adapter->VerifierExtension->NonCachedBufferSize != 0) {
|
|
//
|
|
// Init uncached extension mapping info.
|
|
//
|
|
|
|
Size = (ULONG)ROUND_TO_PAGES(Adapter->VerifierExtension->NonCachedBufferSize);
|
|
MappingInfo = (PSP_VA_MAPPING_INFO)((PUCHAR)BlkAddr[i] + (Size - PAGE_SIZE));
|
|
MappingInfo->OriginalSrbExtVa = BlkAddr[i];
|
|
MappingInfo->SrbExtLen = Adapter->VerifierExtension->NonCachedBufferSize;
|
|
MappingInfo->OriginalSenseVa = (PUCHAR)BlkAddr[i] + Adapter->VerifierExtension->NonCachedBufferSize;
|
|
}
|
|
}
|
|
|
|
PVOID
|
|
SpAllocateContiguousChunk(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDMA_ADAPTER DmaAdapterObject,
|
|
IN BOOLEAN Dma64BitAddresses,
|
|
IN ULONG Length,
|
|
IN ULONG Align,
|
|
OUT PHYSICAL_ADDRESS *PhysicalCommonBuffer,
|
|
OUT BOOLEAN *CommonBuffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a chunk of memory which can be used for common
|
|
buffer io. Where the memory is allocated from depends on several
|
|
parameters. If no adapter object is specified, the memory is simply
|
|
allocated from non-paged pool. Else, the memory is allocated such
|
|
that it can be used in DMA operations.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies a pointer to the driver object.
|
|
|
|
DmaAdapterObject - Supplies a pointer to the adapter's DMA adapter
|
|
object.
|
|
|
|
Dma64BitAddresses - Specifies whether the adapter supports 64-bit.
|
|
|
|
Length - Specifies the number of bytes to allocate.
|
|
|
|
Align - Alignment requirement for uncached extension.
|
|
|
|
PhysicalCommonBuffer - Specifies a pointer into which the physical
|
|
address of the allocated memory is to be copied
|
|
if the memory is allocated for DMA operations.
|
|
|
|
CommonBuffer - Supplies a pointer to a boolean that we set if the
|
|
memory is allocated using AllocateCommonBuffer.
|
|
|
|
Return Value:
|
|
|
|
Returns the VA of the allocated memory if the allocation succeeds. Else,
|
|
returns NULL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID Buffer;
|
|
|
|
if (DmaAdapterObject == NULL) {
|
|
|
|
//
|
|
// Since there is no adapter object just allocate from non-paged pool.
|
|
//
|
|
|
|
Buffer = SpAllocatePool(
|
|
NonPagedPool,
|
|
Length,
|
|
SCSIPORT_TAG_COMMON_BUFFER,
|
|
DriverObject);
|
|
} else {
|
|
|
|
ASSERT(PhysicalCommonBuffer != NULL);
|
|
|
|
//
|
|
// If the controller can do 64-bit addresses then we need to
|
|
// specifically force the uncached extension area below the 4GB mark.
|
|
//
|
|
|
|
if (((Sp64BitPhysicalAddresses) && (Dma64BitAddresses == TRUE)) ||
|
|
Align != 0) {
|
|
|
|
PHYSICAL_ADDRESS low;
|
|
PHYSICAL_ADDRESS high;
|
|
PHYSICAL_ADDRESS boundary;
|
|
|
|
if (Align != 0) {
|
|
boundary.QuadPart = Length;
|
|
} else {
|
|
boundary.QuadPart = 0;
|
|
}
|
|
|
|
low.QuadPart = 0;
|
|
high.HighPart = 0;
|
|
high.LowPart = 0xffffffff;
|
|
|
|
//
|
|
// We'll get page aligned memory out of this which is probably
|
|
// better than the requirements of the adapter.
|
|
//
|
|
|
|
Buffer = MmAllocateContiguousMemorySpecifyCache(
|
|
Length,
|
|
low,
|
|
high,
|
|
boundary,
|
|
MmCached);
|
|
|
|
if (Buffer != NULL) {
|
|
*PhysicalCommonBuffer = MmGetPhysicalAddress(Buffer);
|
|
}
|
|
|
|
if (CommonBuffer != NULL) {
|
|
*CommonBuffer = FALSE;
|
|
}
|
|
|
|
} else {
|
|
Buffer = AllocateCommonBuffer(
|
|
DmaAdapterObject,
|
|
Length,
|
|
PhysicalCommonBuffer,
|
|
FALSE);
|
|
|
|
if (CommonBuffer != NULL) {
|
|
*CommonBuffer = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
NTSTATUS
|
|
SpGetCommonBufferVrfy(
|
|
PADAPTER_EXTENSION DeviceExtension,
|
|
ULONG NonCachedExtensionSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allocates multiple common buffer blocks instead of one
|
|
big one. The verifier does this so it can remap VA ranges within
|
|
each block in order to control their protection attributes. This
|
|
enables us to invalidate key VA ranges and catch miniports that attempt
|
|
to access these ranges when they should not.
|
|
|
|
If the remapping succeeds, the SCSI port driver hands out the remapped
|
|
VA ranges to miniports instead of the original ranges. If the remapping
|
|
fails, it just hands out the original ranges.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the device extension.
|
|
|
|
NonCachedExtensionSize - Supplies the size of the noncached device
|
|
extension for the miniport driver.
|
|
|
|
Return Value:
|
|
|
|
Returns the status of the allocate operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID buffer;
|
|
ULONG length;
|
|
ULONG blockSize;
|
|
PVOID *srbExtension;
|
|
PVOID buffer2;
|
|
PMDL mdl;
|
|
ULONG TotalSize;
|
|
ULONG i;
|
|
PVOID* BlkAddr;
|
|
PHYSICAL_ADDRESS *PhysicalCommonBuffer;
|
|
PCCHAR InvalidRegion;
|
|
BOOLEAN commonBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((1, "SpGetCommonBufferVrfy: DeviceExtension:%p NonCachedExtensionSize:%d\n",
|
|
DeviceExtension, NonCachedExtensionSize));
|
|
|
|
//
|
|
// Now fixup the size if the adapter has special alignment requirements so
|
|
// the buffer we allocate may be aligned as required.
|
|
//
|
|
|
|
if (DeviceExtension->UncachedExtAlignment != 0) {
|
|
NonCachedExtensionSize =
|
|
ROUND_UP_COUNT(NonCachedExtensionSize,
|
|
DeviceExtension->UncachedExtAlignment);
|
|
}
|
|
|
|
//
|
|
// We maintain a couple of arrays in order to find our common
|
|
// buffer blocks at various times. Calculate the amount of space we
|
|
// need for these arrays. This amount depends on the number of
|
|
// simultaneous requests the adapter supports. We add one to the
|
|
// number of requests in order to accommodate the non-cached extension.
|
|
//
|
|
|
|
ASSERT(DeviceExtension->VerifierExtension->CommonBufferVAs == NULL);
|
|
|
|
i = DeviceExtension->NumberOfRequests + 1;
|
|
length = sizeof(PVOID) * i;
|
|
|
|
if (DeviceExtension->DmaAdapterObject != NULL) {
|
|
ASSERT(DeviceExtension->VerifierExtension->CommonBufferPAs == NULL);
|
|
length += (sizeof(PHYSICAL_ADDRESS) * i);
|
|
}
|
|
|
|
//
|
|
// Allocate a block of memory for these arrays. If this allocation fails,
|
|
// we return failure.
|
|
//
|
|
|
|
BlkAddr = SpAllocatePool(NonPagedPool,
|
|
length,
|
|
SCSIPORT_TAG_COMMON_BUFFER,
|
|
DeviceExtension->DeviceObject->DriverObject);
|
|
|
|
if (BlkAddr == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Save the number of common buffer blocks.
|
|
//
|
|
|
|
DeviceExtension->VerifierExtension->CommonBufferBlocks =
|
|
DeviceExtension->NumberOfRequests;
|
|
|
|
//
|
|
// Zero the entire block so when we're freeing resources we can tell if we
|
|
// have valid buffers to free.
|
|
//
|
|
|
|
RtlZeroMemory(BlkAddr, length);
|
|
|
|
//
|
|
// Save a pointer to the array of addresses in the adapter extension and,
|
|
// if there is an adapter object, initialize a pointer to the beginning of
|
|
// the physical address array and save a pointer to the array in the
|
|
// adapter extension.
|
|
//
|
|
|
|
DeviceExtension->VerifierExtension->CommonBufferVAs = (PVOID *)BlkAddr;
|
|
if (DeviceExtension->DmaAdapterObject != NULL) {
|
|
PhysicalCommonBuffer = (PHYSICAL_ADDRESS*) &BlkAddr[i];
|
|
DeviceExtension->VerifierExtension->CommonBufferPAs = PhysicalCommonBuffer;
|
|
}
|
|
|
|
//
|
|
// To ensure that we never transfer normal request data to the SrbExtension
|
|
// (ie. the case of Srb->SenseInfoBuffer == VirtualAddress in
|
|
// ScsiPortGetPhysicalAddress) on some platforms where an inconsistency in
|
|
// MM can result in the same Virtual address supplied for 2 different
|
|
// physical addresses, bump the SrbExtensionSize if it's zero.
|
|
//
|
|
|
|
if (DeviceExtension->SrbExtensionSize == 0) {
|
|
DeviceExtension->SrbExtensionSize = 16;
|
|
}
|
|
|
|
//
|
|
// Calculate the block size for an SRB extension/sense buffer block. If
|
|
// AutoRequestSense is FALSE, allocate 1 page anyway as a placeholder.
|
|
//
|
|
|
|
blockSize = (ULONG)ROUND_TO_PAGES(DeviceExtension->SrbExtensionSize);
|
|
if (DeviceExtension->AutoRequestSense == TRUE) {
|
|
blockSize += sizeof(SENSE_DATA) + DeviceExtension->AdditionalSenseBytes;
|
|
blockSize = (ULONG)ROUND_TO_PAGES(blockSize);
|
|
} else {
|
|
blockSize += PAGE_SIZE;
|
|
}
|
|
|
|
//
|
|
// Add a page for holding bookkeeping information.
|
|
//
|
|
|
|
blockSize += PAGE_SIZE;
|
|
|
|
//
|
|
// Allocate each block individually and link them all together into a
|
|
// list. If we fail to allocate any of the blocks, we clean everything up
|
|
// and return failure.
|
|
//
|
|
|
|
DeviceExtension->CommonBufferSize = blockSize;
|
|
srbExtension = NULL;
|
|
|
|
for (i = 0; i < DeviceExtension->NumberOfRequests; i++) {
|
|
|
|
//
|
|
// Allocate a contiguous chunk of memory for the block.
|
|
//
|
|
|
|
buffer = SpAllocateContiguousChunk(
|
|
DeviceExtension->DeviceObject->DriverObject,
|
|
DeviceExtension->DmaAdapterObject,
|
|
DeviceExtension->Dma64BitAddresses,
|
|
blockSize,
|
|
0,
|
|
(DeviceExtension->DmaAdapterObject) ? &PhysicalCommonBuffer[i] : NULL,
|
|
&commonBuffer);
|
|
|
|
if (buffer == NULL) {
|
|
|
|
//
|
|
// Free everything we've allocated so far and return failure. This
|
|
// will also free the arrays we allocated at the beginning of this
|
|
// function.
|
|
//
|
|
|
|
DeviceExtension->VerifierExtension->IsCommonBuffer = commonBuffer;
|
|
SpFreeCommonBufferVrfy(DeviceExtension);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Zero the entire block and save a pointer to it in our array.
|
|
//
|
|
|
|
RtlZeroMemory(buffer, blockSize);
|
|
BlkAddr[i] = buffer;
|
|
|
|
//
|
|
// Link the new block onto the front of the chain.
|
|
//
|
|
|
|
*((PVOID *) buffer) = srbExtension;
|
|
srbExtension = (PVOID *) buffer;
|
|
}
|
|
|
|
//
|
|
// Indicate whether the buffer was allocated as common buffer.
|
|
//
|
|
|
|
DeviceExtension->VerifierExtension->IsCommonBuffer = commonBuffer;
|
|
|
|
//
|
|
// Allocate the non-cached extension. Note that we align the uncached
|
|
// buffer on the next page boundary and allocate enough for a scratch page.
|
|
// If the allocation fails, free everything we've allocated so far and
|
|
// return failure.
|
|
//
|
|
|
|
if (NonCachedExtensionSize != 0) {
|
|
|
|
DeviceExtension->VerifierExtension->NonCachedBufferSize = NonCachedExtensionSize;
|
|
length = (ULONG)(ROUND_TO_PAGES(NonCachedExtensionSize));
|
|
|
|
BlkAddr[i] =
|
|
SpAllocateContiguousChunk(
|
|
DeviceExtension->DeviceObject->DriverObject,
|
|
DeviceExtension->DmaAdapterObject,
|
|
DeviceExtension->Dma64BitAddresses,
|
|
length,
|
|
DeviceExtension->UncachedExtAlignment,
|
|
(DeviceExtension->DmaAdapterObject) ? &PhysicalCommonBuffer[i] : NULL,
|
|
&DeviceExtension->UncachedExtensionIsCommonBuffer);
|
|
|
|
if (BlkAddr[i] == NULL) {
|
|
|
|
//
|
|
// Free everything we've allocated so far and return failure. This
|
|
// will also free the arrays we allocated at the beginning of this
|
|
// function.
|
|
//
|
|
|
|
SpFreeCommonBufferVrfy(DeviceExtension);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Zero the entire block.
|
|
//
|
|
|
|
RtlZeroMemory(BlkAddr[i], length);
|
|
|
|
//
|
|
// Save a pointer to the beginning of the non-cached extension data.
|
|
// Note that the data is positioned such that it ends on a page
|
|
// boundary so if the miniport overwrites the buffer, the system will
|
|
// fault.
|
|
//
|
|
|
|
DeviceExtension->NonCachedExtension =
|
|
(PCCHAR)BlkAddr[i] +
|
|
(ROUND_TO_PAGES(NonCachedExtensionSize) - NonCachedExtensionSize);
|
|
|
|
} else {
|
|
|
|
DeviceExtension->NonCachedExtension = NULL;
|
|
DeviceExtension->VerifierExtension->NonCachedBufferSize = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// If the miniport asked for an SRB Extension, point the SRB Extension List
|
|
// at the beginning of the list of blocks we allocated and chained together
|
|
// above.
|
|
//
|
|
|
|
if (DeviceExtension->AllocateSrbExtension == TRUE) {
|
|
DeviceExtension->SrbExtensionListHeader = srbExtension;
|
|
} else {
|
|
ASSERT(DeviceExtension->SrbExtensionListHeader == NULL);
|
|
}
|
|
|
|
//
|
|
// Create a second VA mapping of the common buffer area so we can make the
|
|
// range of addresses invalid when the miniport is not supposed to touch it.
|
|
// This will allow us to catch mis-behaving miniports.
|
|
//
|
|
|
|
SpRemapCommonBufferForMiniport(DeviceExtension);
|
|
|
|
DebugPrint((1, "SpGetCommonBufferVrfy: returning STATUS_SUCCESS\n"));
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
VOID
|
|
SpFreeCommonBufferVrfy(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees all of the common buffer space we've allocated for the
|
|
miniport on the supplied adapter. If only partially allocated, the routine
|
|
correctly cleans up the parts that are present. On exit, all the memory has
|
|
been freed and the associated pointers have been NULLed.
|
|
|
|
Arguments:
|
|
|
|
DeviceExtension - Supplies a pointer to the adapter device extension.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
PVOID* BlkAddr;
|
|
NTSTATUS Status;
|
|
PSP_VA_MAPPING_INFO MappingInfo;
|
|
|
|
ASSERT(Adapter->SrbExtensionBuffer == NULL);
|
|
|
|
if (Adapter->VerifierExtension != NULL &&
|
|
Adapter->VerifierExtension->CommonBufferVAs != NULL) {
|
|
//
|
|
// Initialize a pointer to the array of pointers we use to track and
|
|
// manage the common buffer blocks.
|
|
//
|
|
|
|
BlkAddr = Adapter->VerifierExtension->CommonBufferVAs;
|
|
|
|
//
|
|
// Cycle through the array of common memory descriptors, freeing each
|
|
// one. What we are freeing here is the SRB Extension/Sense Data
|
|
// buffers. Stop when we've deleted all the blocks.
|
|
//
|
|
|
|
for (i = 0; i < Adapter->VerifierExtension->CommonBufferBlocks && BlkAddr[i]; i++) {
|
|
|
|
//
|
|
// If there is a second VA range for the common block, free the
|
|
// MDL(s).
|
|
//
|
|
|
|
MappingInfo = GET_VA_MAPPING_INFO(Adapter, BlkAddr[i]);
|
|
|
|
if (MappingInfo->SrbExtMdl != NULL) {
|
|
MmProtectMdlSystemAddress(MappingInfo->SrbExtMdl, PAGE_READWRITE);
|
|
MmUnlockPages(MappingInfo->SrbExtMdl);
|
|
IoFreeMdl(MappingInfo->SrbExtMdl);
|
|
}
|
|
|
|
if (MappingInfo->SenseMdl != NULL) {
|
|
MmProtectMdlSystemAddress(MappingInfo->SrbExtMdl, PAGE_READWRITE);
|
|
MmUnlockPages(MappingInfo->SenseMdl);
|
|
IoFreeMdl(MappingInfo->SenseMdl);
|
|
}
|
|
|
|
//
|
|
// Free the memory. The method we use depends on how the memory
|
|
// was allocated.
|
|
//
|
|
|
|
if (Adapter->DmaAdapterObject == NULL) {
|
|
ExFreePool(BlkAddr[i]);
|
|
} else {
|
|
if (Adapter->VerifierExtension->IsCommonBuffer == FALSE) {
|
|
MmFreeContiguousMemorySpecifyCache(
|
|
BlkAddr[i],
|
|
Adapter->CommonBufferSize,
|
|
MmCached);
|
|
} else {
|
|
FreeCommonBuffer(
|
|
Adapter->DmaAdapterObject,
|
|
Adapter->CommonBufferSize,
|
|
Adapter->VerifierExtension->CommonBufferPAs[i],
|
|
BlkAddr[i],
|
|
FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the uncached extension if we allocated one.
|
|
//
|
|
|
|
if (Adapter->NonCachedExtension != NULL) {
|
|
|
|
ULONG Length;
|
|
|
|
//
|
|
// Calculate the total length of the non-cached extension block we
|
|
// allocated. This is the non-cached buffer size asked for by the
|
|
// miniport rounded up to the next page boundary plus one full page.
|
|
//
|
|
|
|
Length = (ULONG)(ROUND_TO_PAGES(Adapter->VerifierExtension->NonCachedBufferSize));
|
|
|
|
//
|
|
// Free the memory. The method we use depends on how the memory
|
|
// was allocated.
|
|
//
|
|
|
|
if (Adapter->DmaAdapterObject == NULL) {
|
|
ExFreePool(BlkAddr[i]);
|
|
} else {
|
|
if (Adapter->UncachedExtensionIsCommonBuffer == FALSE) {
|
|
MmFreeContiguousMemorySpecifyCache(
|
|
BlkAddr[i],
|
|
Length,
|
|
MmCached);
|
|
} else {
|
|
FreeCommonBuffer(
|
|
Adapter->DmaAdapterObject,
|
|
Length,
|
|
Adapter->VerifierExtension->CommonBufferPAs[i],
|
|
BlkAddr[i],
|
|
FALSE);
|
|
}
|
|
}
|
|
|
|
Adapter->NonCachedExtension = NULL;
|
|
}
|
|
|
|
//
|
|
// Free the arrays we allocated to manage the common buffer area.
|
|
//
|
|
|
|
ExFreePool(Adapter->VerifierExtension->CommonBufferVAs);
|
|
Adapter->VerifierExtension->CommonBufferVAs = NULL;
|
|
Adapter->VerifierExtension->CommonBufferPAs = NULL;
|
|
Adapter->VerifierExtension->CommonBufferBlocks = 0;
|
|
Adapter->SrbExtensionListHeader = NULL;
|
|
}
|
|
}
|
|
|
|
PVOID
|
|
SpGetOriginalSrbExtVa(
|
|
PADAPTER_EXTENSION Adapter,
|
|
PVOID Va
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the original mapped virtual address of a common
|
|
block if the supplied VA is for one of the common buffer blocks we've
|
|
allocated.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter device extension
|
|
|
|
Va - virtual address of a common buffer block
|
|
|
|
Return Value:
|
|
|
|
If the supplied VA is the address of one of the common buffer blocks,
|
|
returns the original VA of the block. Else, returns NULL.
|
|
|
|
--*/
|
|
{
|
|
PVOID* BlkAddr = Adapter->VerifierExtension->CommonBufferVAs;
|
|
PSP_VA_MAPPING_INFO MappingInfo;
|
|
ULONG i;
|
|
|
|
for (i = 0; i < Adapter->VerifierExtension->CommonBufferBlocks; i++) {
|
|
MappingInfo = GET_VA_MAPPING_INFO(Adapter, *BlkAddr++);
|
|
if (Va == MappingInfo->RemappedSrbExtVa ||
|
|
Va == MappingInfo->OriginalSrbExtVa)
|
|
return MappingInfo->OriginalSrbExtVa;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
SpInsertSrbExtension(
|
|
PADAPTER_EXTENSION Adapter,
|
|
PCCHAR SrbExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine inserts the supplied SRB extension back into the SRB extension
|
|
list. The VA of the supplied extension lies within one of our common buffer
|
|
blocks and it may be a remapped VA. If it is a remapped address, this
|
|
routine invalidates the page(s) comprising the extension after it links the
|
|
extension back into the list.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Pointer to an adapter device extension.
|
|
|
|
SrbExtension - Pointer to the beginning of an SRB extension within one of
|
|
our common buffer blocks. May or may not be within a
|
|
remapped range.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Round the srb extension pointer down to the beginning of the page
|
|
// and link the block back into the list. Note that we're careful
|
|
// to point the list header at the original VA of the block.
|
|
//
|
|
|
|
SrbExtension = (PVOID)((ULONG_PTR)SrbExtension & ~(PAGE_SIZE - 1));
|
|
*((PVOID *) SrbExtension) = Adapter->SrbExtensionListHeader;
|
|
Adapter->SrbExtensionListHeader = SpGetOriginalSrbExtVa(
|
|
Adapter,
|
|
SrbExtension);
|
|
|
|
//
|
|
// If the original VA differs from the one supplied, the supplied
|
|
// one is one of our remapped VAs. In this case, we want to invalidate
|
|
// the range so the system will bugcheck if anyone tries to access it.
|
|
//
|
|
|
|
if (Adapter->SrbExtensionListHeader != SrbExtension) {
|
|
PMDL Mdl = SpGetRemappedSrbExt(Adapter, Adapter->SrbExtensionListHeader);
|
|
ASSERT(Mdl != NULL);
|
|
MmProtectMdlSystemAddress(Mdl, PAGE_NOACCESS);
|
|
|
|
//
|
|
// Just because we remapped the SRB extension does not mean we
|
|
// necessarily remapped the sense buffer.
|
|
//
|
|
|
|
Mdl = SpGetRemappedSenseBuffer(Adapter, Adapter->SrbExtensionListHeader);
|
|
if (Mdl != NULL) {
|
|
MmProtectMdlSystemAddress(Mdl, PAGE_NOACCESS);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
ULONG SpVerbose = 0;
|
|
#endif
|
|
|
|
PVOID
|
|
SpPrepareSrbExtensionForUse(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN OUT PCCHAR *SrbExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function accepts a pointer to the beginning of one of the individual
|
|
common-buffer blocks allocated by the verifier for SRB extensions, sense
|
|
buffers, and non-cached extensions. It calculates the beginning of the
|
|
SRB extension within the block and, if the block has been remapped, makes
|
|
the page(s) of the SRB extension read/write valid.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Pointer to an adapter device extension.
|
|
|
|
SrbExtension - Pointer to the beginning of a common-buffer block.
|
|
|
|
Return Value:
|
|
|
|
If the common buffer block containing the SRB extension has been remapped,
|
|
returns the address of the beginning of the remapped srb extension, valid
|
|
for reading and writing.
|
|
|
|
If the block has not been remapped, returns NULL.
|
|
|
|
Regardless of whether the block is remapped or not, the supplied pointer
|
|
is fixed up to point to the beginning of the SRB extension within the
|
|
original VA range.
|
|
|
|
--*/
|
|
{
|
|
PCCHAR RemappedSrbExt = NULL;
|
|
NTSTATUS Status;
|
|
PMDL Mdl;
|
|
ULONG srbExtensionSize = ROUND_UP_COUNT(Adapter->SrbExtensionSize, 8);
|
|
|
|
//
|
|
// If we've remapped the SRB extension, get the second mapping and make it
|
|
// valid. If we get the second mapping, but cannot make it valid, we just
|
|
// use the original mapping.
|
|
//
|
|
|
|
Mdl = SpGetRemappedSrbExt(Adapter, *SrbExtension);
|
|
if (Mdl != NULL) {
|
|
Status = MmProtectMdlSystemAddress(Mdl, PAGE_READWRITE);
|
|
if (NT_SUCCESS(Status)) {
|
|
RemappedSrbExt = MmGetSystemAddressForMdlSafe(
|
|
Mdl,
|
|
NormalPagePriority);
|
|
|
|
//
|
|
// Adjust the remapped srb extension pointer so the end of the
|
|
// buffer falls on a page boundary.
|
|
//
|
|
|
|
RemappedSrbExt +=
|
|
((Adapter->CommonBufferSize -
|
|
(PAGE_SIZE * 2)) - srbExtensionSize);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Adjust the original srb extension pointer so it also ends on a page
|
|
// boundary.
|
|
//
|
|
|
|
*SrbExtension += ((Adapter->CommonBufferSize - (PAGE_SIZE * 2)) -
|
|
srbExtensionSize);
|
|
#if DBG
|
|
if (SpVerbose == 1) {
|
|
DebugPrint((0, "SpPrepareSrbExtensionForUse: SrbExt %p SrbExtSize %x\n",
|
|
*SrbExtension, srbExtensionSize));
|
|
}
|
|
#endif
|
|
return RemappedSrbExt;
|
|
}
|
|
|
|
PCCHAR
|
|
SpPrepareSenseBufferForUse(
|
|
PADAPTER_EXTENSION Adapter,
|
|
PCCHAR SrbExtension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function accepts a pointer to the beginning of an SRB extension
|
|
within one of the individual common-buffer blocks allocated by the
|
|
verifier for SRB extensions, sense buffers, and non-cached extensions.
|
|
It calculates the beginning of the sense buffer within the block and,
|
|
if the block has been remapped, makes the page read/write valid.
|
|
|
|
It is assumed that a sense buffer will never be larger than one page.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Pointer to an adapter device extension.
|
|
|
|
SrbExtension - Pointer to the beginning of the SRB extension within a
|
|
common-buffer block.
|
|
|
|
Return Value:
|
|
|
|
Returns the address of the beginning of a sense buffer valid for
|
|
reading and writing.
|
|
|
|
--*/
|
|
{
|
|
PVOID BeginningOfBlock;
|
|
ULONG SenseDataSize;
|
|
PCCHAR Base;
|
|
NTSTATUS Status;
|
|
PMDL Mdl;
|
|
ULONG srbExtensionSize = (ULONG)ROUND_TO_PAGES(Adapter->SrbExtensionSize);
|
|
|
|
//
|
|
// Initialize the size of the sense buffer and the base of the sense buffer
|
|
// within the originally allocated block. The base of the sense buffer
|
|
// immediately follows the srb extension and resides on a page boundary
|
|
// within a common buffer block.
|
|
//
|
|
|
|
SenseDataSize = sizeof(SENSE_DATA) + Adapter->AdditionalSenseBytes;
|
|
SenseDataSize = ROUND_UP_COUNT(SenseDataSize, 8);
|
|
Base = SrbExtension + ROUND_UP_COUNT(Adapter->SrbExtensionSize, 8);
|
|
|
|
//
|
|
// Initialize a pointer to the beginning of the common block the sense
|
|
// buffer resides in. This is needed in order to determine if the
|
|
// sense buffer has been remapped.
|
|
//
|
|
|
|
BeginningOfBlock =
|
|
(PVOID)(((ULONG_PTR)SrbExtension +
|
|
ROUND_UP_COUNT(Adapter->SrbExtensionSize, 8)) -
|
|
srbExtensionSize);
|
|
|
|
//
|
|
// If we've remapped the sense buffer, make the range valid and reset base
|
|
// to point to the beginning of the range.
|
|
//
|
|
|
|
Mdl = SpGetRemappedSenseBuffer(Adapter, BeginningOfBlock);
|
|
if (Mdl != NULL) {
|
|
Status = MmProtectMdlSystemAddress(Mdl, PAGE_READWRITE);
|
|
if (NT_SUCCESS(Status)) {
|
|
Base = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
|
|
ASSERT(Base != NULL);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if (SpVerbose == 1) {
|
|
DebugPrint((0, "SpPrepareSenseBufferForUse: SrbExt %p Base %p BOB %p "
|
|
"SenseBuffer %p SrbExtSize %x\n",
|
|
SrbExtension,
|
|
Base,
|
|
BeginningOfBlock,
|
|
(Base + PAGE_SIZE - SenseDataSize),
|
|
srbExtensionSize));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Return a pointer into the block such that the sense buffer ends aligned
|
|
// on a page boundary.
|
|
//
|
|
|
|
return (Base + PAGE_SIZE - SenseDataSize);
|
|
}
|
|
|
|
PVOID
|
|
SpGetInaccessiblePage(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns a pointer to a page of memory that is not valid
|
|
for reading or writing. This page is a shared resource, used by all
|
|
adapters that are actively verifying. The page is hung off of the driver
|
|
extension. The page is faulted in as needed, so if we haven't initialized
|
|
it yet, we try to do so here in an interlocked fashion.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Pointer to an adapter device extension.
|
|
|
|
Return Value:
|
|
|
|
Either returns a pointer to an invalid page of VAs or NULL if the page
|
|
could not be allocated.
|
|
|
|
--*/
|
|
{
|
|
PSCSIPORT_DRIVER_EXTENSION DriverExtension;
|
|
PVOID UnusedPage;
|
|
PVOID InvalidPage;
|
|
PMDL UnusedPageMdl;
|
|
PVOID CurrentValue;
|
|
|
|
//
|
|
// Retrieve the driver extension. We must have it to proceed.
|
|
//
|
|
|
|
DriverExtension = IoGetDriverObjectExtension(
|
|
Adapter->DeviceObject->DriverObject,
|
|
ScsiPortInitialize);
|
|
if (DriverExtension == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// If the invalid page is not yet initialized, go ahead and try to
|
|
// initialize it now.
|
|
//
|
|
|
|
if (DriverExtension->InvalidPage == NULL) {
|
|
|
|
//
|
|
// Allocate a page of memory.
|
|
//
|
|
|
|
UnusedPage = SpAllocatePool(NonPagedPool,
|
|
PAGE_SIZE,
|
|
SCSIPORT_TAG_VERIFIER,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if (UnusedPage != NULL) {
|
|
|
|
//
|
|
// Zero the page and remap it. The remapped range will be inaccessible.
|
|
// If the remapping fails, just free the page; we just won't have an
|
|
// inaccessible page to work with.
|
|
//
|
|
|
|
RtlZeroMemory(UnusedPage, PAGE_SIZE);
|
|
InvalidPage = SpRemapBlock(UnusedPage,
|
|
PAGE_SIZE,
|
|
&UnusedPageMdl);
|
|
|
|
if (InvalidPage != NULL) {
|
|
|
|
//
|
|
// If nobody else has beaten us to it, init the pointer to the
|
|
// invalid page in the driver extension. If somebody has already
|
|
// done it, just free the page we created. This page is freed
|
|
// when scsiport is unloaded.
|
|
//
|
|
|
|
CurrentValue = InterlockedCompareExchangePointer(
|
|
&DriverExtension->InvalidPage,
|
|
InvalidPage,
|
|
NULL);
|
|
if (CurrentValue == NULL) {
|
|
|
|
DriverExtension->UnusedPage = UnusedPage;
|
|
DriverExtension->UnusedPageMdl = UnusedPageMdl;
|
|
|
|
} else {
|
|
|
|
MmProtectMdlSystemAddress(UnusedPageMdl, PAGE_READWRITE);
|
|
UnusedPageMdl->MdlFlags &= ~MDL_MAPPED_TO_SYSTEM_VA;
|
|
IoFreeMdl(UnusedPageMdl);
|
|
ExFreePool(UnusedPage);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Couldn't make the page inaccessible, just free it.
|
|
//
|
|
|
|
ExFreePool(UnusedPage);
|
|
}
|
|
}
|
|
}
|
|
|
|
return DriverExtension->InvalidPage;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpCheckForActiveRequests(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function walks through all of the logical units connected to the
|
|
supplied adapter looking for any outstanding requests. If it finds
|
|
any, it returns TRUE immediately.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Pointer to an adapter device extension.
|
|
|
|
Return Value:
|
|
|
|
TRUE - If an outstanding requests is found on one of the logical units
|
|
connected to the adapter.
|
|
|
|
FALSE - If no outstanding requests on the adapter.
|
|
|
|
--*/
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION LogicalUnit;
|
|
PLOGICAL_UNIT_BIN Bin;
|
|
ULONG BinNumber;
|
|
|
|
//
|
|
// Iterate through each LU bin. For each bin, if there are any LUs, iterate
|
|
// through each of those looking for an oustanding request. If we find one
|
|
// terminate the search and return TRUE.
|
|
//
|
|
|
|
for (BinNumber = 0; BinNumber < NUMBER_LOGICAL_UNIT_BINS; BinNumber++) {
|
|
|
|
Bin = &Adapter->LogicalUnitList[BinNumber];
|
|
|
|
LogicalUnit = Bin->List;
|
|
while (LogicalUnit != NULL) {
|
|
|
|
if (LogicalUnit->AbortSrb != NULL &&
|
|
LogicalUnit->AbortSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
|
|
return TRUE;
|
|
} else if (LogicalUnit->CurrentUntaggedRequest != NULL &&
|
|
LogicalUnit->CurrentUntaggedRequest->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
|
|
return TRUE;
|
|
} else if (LogicalUnit->RequestList.Flink != &LogicalUnit->RequestList) {
|
|
PSRB_DATA srbData;
|
|
PVOID nextEntry = LogicalUnit->RequestList.Flink;
|
|
while (nextEntry != &LogicalUnit->RequestList) {
|
|
srbData = CONTAINING_RECORD(nextEntry, SRB_DATA, RequestList);
|
|
if (srbData->CurrentSrb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
|
|
return TRUE;
|
|
}
|
|
nextEntry = srbData->RequestList.Flink;
|
|
}
|
|
}
|
|
|
|
LogicalUnit = LogicalUnit->NextLogicalUnit;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
SpEnsureAllRequestsAreComplete(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine bugchecks the system if there are any outstanding requests
|
|
on the supplied adapter. If the SP_DONT_CHK_REQUESTS_ON_RESET bit is
|
|
set on the adapter's verification level, don't do the check.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Points to an adapter device extension.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If there are any outstanding requests on any of the LUs connected to the
|
|
// adapter, bugcheck the system. Note that we only do this check if it
|
|
// has not been turned off.
|
|
//
|
|
|
|
if (VRFY_DO_CHECK(Adapter, SP_DONT_CHK_REQUESTS_ON_RESET)) {
|
|
BOOLEAN ActiveRequests = SpCheckForActiveRequests(Adapter);
|
|
if (ActiveRequests == TRUE) {
|
|
KeBugCheckEx(SCSI_VERIFIER_DETECTED_VIOLATION,
|
|
SCSIPORT_VERIFIER_RQSTS_NOT_COMPLETE,
|
|
(ULONG_PTR)Adapter,
|
|
(ULONG_PTR)Adapter->HwDeviceExtension,
|
|
0);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SpDoVerifierInit(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PHW_INITIALIZATION_DATA HwInitializationData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates and initializes a verifier extension for the
|
|
supplied adapter. A per-adapter verification level is read from the
|
|
registry before allocating the extension. A verfication level of -1
|
|
means "don't verify this adapter". If we do allocate the extension,
|
|
we also lock the verifier code section into memory.
|
|
|
|
Arguments:
|
|
|
|
Adapter - The adapter device extension.
|
|
|
|
HwInitializationData - A pointer to the HW_INITIALIZATION_DATA for
|
|
the adapter.
|
|
|
|
--*/
|
|
{
|
|
ULONG VerifyLevel;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Read adapter's verification level from the registry. If the adapter is
|
|
// configured for no verification, just return.
|
|
//
|
|
|
|
VerifyLevel = SpGetAdapterVerifyLevel(Adapter);
|
|
if (VerifyLevel == SP_VRFY_NONE) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Go ahead and try to allocate the extension.
|
|
//
|
|
|
|
Adapter->VerifierExtension =
|
|
SpAllocatePool(NonPagedPool,
|
|
sizeof(VERIFIER_EXTENSION),
|
|
SCSIPORT_TAG_VERIFIER,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if (Adapter->VerifierExtension != NULL) {
|
|
|
|
//
|
|
// Zero the extension.
|
|
//
|
|
|
|
RtlZeroMemory(Adapter->VerifierExtension, sizeof(VERIFIER_EXTENSION));
|
|
|
|
//
|
|
// Lock the pageable verifier code section into memory.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
if (VerifierCodeSectionHandle == NULL) {
|
|
VerifierCodeSectionHandle = MmLockPagableCodeSection(SpHwFindAdapterVrfy);
|
|
} else {
|
|
MmLockPagableSectionByHandle(VerifierCodeSectionHandle);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set the verification level for this adapter. This value is the sum
|
|
// of the global verifier level and the per-adapter value we read above.
|
|
//
|
|
|
|
Adapter->VerifierExtension->VrfyLevel = (VerifyLevel | SpVrfyLevel);
|
|
|
|
//
|
|
// Initialize function pointers in the verifier extension to
|
|
// to point to the real miniport routines.
|
|
//
|
|
|
|
Adapter->VerifierExtension->RealHwFindAdapter = HwInitializationData->HwFindAdapter;
|
|
Adapter->VerifierExtension->RealHwInitialize = HwInitializationData->HwInitialize;
|
|
Adapter->VerifierExtension->RealHwStartIo = HwInitializationData->HwStartIo;
|
|
Adapter->VerifierExtension->RealHwInterrupt = HwInitializationData->HwInterrupt;
|
|
Adapter->VerifierExtension->RealHwResetBus = HwInitializationData->HwResetBus;
|
|
Adapter->VerifierExtension->RealHwDmaStarted = HwInitializationData->HwDmaStarted;
|
|
Adapter->VerifierExtension->RealHwAdapterControl = HwInitializationData->HwAdapterControl;
|
|
|
|
//
|
|
// Redirect the miniport routines to verifier routines.
|
|
//
|
|
|
|
Adapter->HwFindAdapter = SpHwFindAdapterVrfy;
|
|
Adapter->HwInitialize = SpHwInitializeVrfy;
|
|
Adapter->HwStartIo = SpHwStartIoVrfy;
|
|
Adapter->HwInterrupt = SpHwInterruptVrfy;
|
|
Adapter->HwResetBus = SpHwResetBusVrfy;
|
|
Adapter->HwDmaStarted = SpHwDmaStartedVrfy;
|
|
Adapter->HwAdapterControl = SpHwAdapterControlVrfy;
|
|
|
|
//
|
|
// Get a pointer to an invalid page of memory so we can catch
|
|
// miniports trying to touch memory when they shouldn't be.
|
|
//
|
|
|
|
Adapter->VerifierExtension->InvalidPage = SpGetInaccessiblePage(Adapter);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
SpDoVerifierCleanup(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees the supplied adapter's verifier extension and releases
|
|
its reference on the verifier code section.
|
|
|
|
This routine gets called as part of the adapter resource cleanup. When
|
|
called, all the actual resources allocated for the verifier have already
|
|
been cleaned up.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter device extension
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// We should never arrive here if the scsiport verifier is not active.
|
|
// And when we get here we should have freed all the resources hanging
|
|
// off the extension.
|
|
//
|
|
|
|
ASSERT(Adapter->VerifierExtension != NULL);
|
|
ASSERT(Adapter->VerifierExtension->CommonBufferVAs == NULL);
|
|
ASSERT(Adapter->VerifierExtension->CommonBufferPAs == NULL);
|
|
ASSERT(Adapter->VerifierExtension->CommonBufferBlocks == 0);
|
|
|
|
//
|
|
// Free and NULL the verifier extension for this adapter.
|
|
//
|
|
|
|
ExFreePool(Adapter->VerifierExtension);
|
|
Adapter->VerifierExtension = NULL;
|
|
|
|
//
|
|
// Release our reference on the verifier code section.
|
|
//
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
ASSERT(VerifierCodeSectionHandle != NULL);
|
|
MmUnlockPagableImageSection(VerifierCodeSectionHandle);
|
|
#endif
|
|
}
|
|
|
|
ULONG
|
|
SpGetAdapterVerifyLevel(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the verification level for the supplied adapter.
|
|
|
|
Arguments:
|
|
|
|
Adapter - Pointer to an adapter device extension.
|
|
|
|
Return Value:
|
|
|
|
The supplied adapter's verification level.
|
|
|
|
--*/
|
|
{
|
|
PSCSIPORT_DRIVER_EXTENSION DrvExt;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING UnicodeString;
|
|
HANDLE ParametersKey;
|
|
HANDLE ServiceKey;
|
|
ULONG VerifyLevel = 0;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// We need the driver extension to get the adapter's registry path. We use
|
|
// this to look up the adapter settings in the registry. If we cannot get
|
|
// the driver extension, we have to abort.
|
|
//
|
|
|
|
DrvExt = IoGetDriverObjectExtension(
|
|
Adapter->DeviceObject->DriverObject,
|
|
ScsiPortInitialize);
|
|
if (DrvExt == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Try to open the adapter's registry key.
|
|
//
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&DrvExt->RegistryPath,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&ServiceKey, KEY_READ, &ObjectAttributes);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Try to open the adapter's parameters key.
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"Parameters");
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ServiceKey,
|
|
NULL);
|
|
|
|
Status = ZwOpenKey(&ParametersKey, KEY_READ, &ObjectAttributes);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// Try to read the verification level value under the adapter's
|
|
// parameters key.
|
|
//
|
|
|
|
RtlInitUnicodeString(&UnicodeString, L"VerifyLevel");
|
|
SpReadNumericValue(
|
|
ParametersKey,
|
|
NULL,
|
|
&UnicodeString,
|
|
&VerifyLevel);
|
|
|
|
ZwClose(ParametersKey);
|
|
}
|
|
|
|
ZwClose(ServiceKey);
|
|
}
|
|
|
|
return VerifyLevel;
|
|
}
|