4228 lines
103 KiB
C
4228 lines
103 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
arbiter.c
|
||
|
||
Abstract:
|
||
|
||
This module contains support routines for the Pnp resource arbiters.
|
||
|
||
Author:
|
||
|
||
Andrew Thornton (andrewth) 1-April-1997
|
||
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "arbp.h"
|
||
|
||
#define REGSTR_KEY_ROOTENUM L"ROOT"
|
||
//
|
||
// Conditional compilation constants
|
||
//
|
||
|
||
#define ALLOW_BOOT_ALLOC_CONFLICTS 1
|
||
#define PLUG_FEST_HACKS 0
|
||
|
||
//
|
||
// Pool Tags
|
||
//
|
||
|
||
#define ARBITER_ALLOCATION_STATE_TAG 'AbrA'
|
||
#define ARBITER_ORDERING_LIST_TAG 'LbrA'
|
||
#define ARBITER_MISC_TAG 'MbrA'
|
||
#define ARBITER_RANGE_LIST_TAG 'RbrA'
|
||
#define ARBITER_CONFLICT_INFO_TAG 'CbrA'
|
||
|
||
//
|
||
// Constants
|
||
//
|
||
|
||
#define PATH_ARBITERS L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Arbiters"
|
||
#define KEY_ALLOCATIONORDER L"AllocationOrder"
|
||
#define KEY_RESERVEDRESOURCES L"ReservedResources"
|
||
#define ARBITER_ORDERING_GROW_SIZE 8
|
||
|
||
|
||
//
|
||
// Macros
|
||
//
|
||
|
||
//
|
||
// PVOID
|
||
// FULL_INFO_DATA(
|
||
// IN PKEY_VALUE_FULL_INFORMATION k
|
||
// );
|
||
//
|
||
// This macro returns the pointer to the beginning of the data area of a
|
||
// KEY_VALUE_FULL_INFORMATION structure.
|
||
//
|
||
|
||
#define FULL_INFO_DATA(k) ((PCHAR)(k) + (k)->DataOffset)
|
||
|
||
//
|
||
// BOOLEAN
|
||
// DISJOINT(
|
||
// IN ULONGLONG s1,
|
||
// IN ULONGLONG e1,
|
||
// IN ULONGLONG s2,
|
||
// IN ULONGLONG e2
|
||
// );
|
||
//
|
||
#define DISJOINT(s1,e1,s2,e2) \
|
||
( ((s1) < (s2) && (e1) < (s2)) \
|
||
||((s2) < (s1) && (e2) < (s1)) )
|
||
|
||
//
|
||
// VOID
|
||
// ArbpWstrToUnicodeString(
|
||
// IN PUNICODE_STRING u,
|
||
// IN PWSTR p
|
||
// );
|
||
//
|
||
|
||
#define ArbpWstrToUnicodeString(u, p) \
|
||
(u)->Length = ((u)->MaximumLength = \
|
||
(USHORT) (sizeof((p))) - sizeof(WCHAR)); \
|
||
(u)->Buffer = (p)
|
||
|
||
//
|
||
// ULONG
|
||
// INDEX_FROM_PRIORITY(
|
||
// LONG Priority
|
||
// );
|
||
//
|
||
|
||
#define ORDERING_INDEX_FROM_PRIORITY(P) \
|
||
( (ULONG) ( (P) > 0 ? (P) - 1 : ((P) * -1) - 1) )
|
||
|
||
//
|
||
// Prototypes
|
||
//
|
||
|
||
NTSTATUS
|
||
ArbpBuildAllocationStack(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PLIST_ENTRY ArbitrationList,
|
||
IN ULONG ArbitrationListCount
|
||
);
|
||
|
||
NTSTATUS
|
||
ArbpGetRegistryValue(
|
||
IN HANDLE KeyHandle,
|
||
IN PWSTR ValueName,
|
||
OUT PKEY_VALUE_FULL_INFORMATION *Information
|
||
);
|
||
|
||
NTSTATUS
|
||
ArbpBuildAlternative(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PIO_RESOURCE_DESCRIPTOR Requirement,
|
||
OUT PARBITER_ALTERNATIVE Alternative
|
||
);
|
||
|
||
VOID
|
||
ArbpUpdatePriority(
|
||
PARBITER_INSTANCE Arbiter,
|
||
PARBITER_ALTERNATIVE Alternative
|
||
);
|
||
|
||
BOOLEAN
|
||
ArbpQueryConflictCallback(
|
||
IN PVOID Context,
|
||
IN PRTL_RANGE Range
|
||
);
|
||
|
||
BOOLEAN
|
||
ArbShareDriverExclusive(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
);
|
||
|
||
//
|
||
// Make everything pageable
|
||
//
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
|
||
VOID
|
||
ArbDereferenceArbiterInstance(
|
||
IN PARBITER_INSTANCE Arbiter
|
||
);
|
||
|
||
#pragma alloc_text(PAGE, ArbInitializeArbiterInstance)
|
||
#pragma alloc_text(PAGE, ArbDereferenceArbiterInstance)
|
||
#pragma alloc_text(PAGE, ArbDeleteArbiterInstance)
|
||
#pragma alloc_text(PAGE, ArbTestAllocation)
|
||
#pragma alloc_text(PAGE, ArbpBuildAlternative)
|
||
#pragma alloc_text(PAGE, ArbpBuildAllocationStack)
|
||
#pragma alloc_text(PAGE, ArbSortArbitrationList)
|
||
#pragma alloc_text(PAGE, ArbCommitAllocation)
|
||
#pragma alloc_text(PAGE, ArbRollbackAllocation)
|
||
#pragma alloc_text(PAGE, ArbRetestAllocation)
|
||
#pragma alloc_text(PAGE, ArbBootAllocation)
|
||
#pragma alloc_text(PAGE, ArbArbiterHandler)
|
||
#pragma alloc_text(PAGE, ArbBuildAssignmentOrdering)
|
||
#pragma alloc_text(PAGE, ArbFindSuitableRange)
|
||
#pragma alloc_text(PAGE, ArbAddAllocation)
|
||
#pragma alloc_text(PAGE, ArbBacktrackAllocation)
|
||
#pragma alloc_text(PAGE, ArbPreprocessEntry)
|
||
#pragma alloc_text(PAGE, ArbAllocateEntry)
|
||
#pragma alloc_text(PAGE, ArbGetNextAllocationRange)
|
||
#pragma alloc_text(PAGE, ArbpGetRegistryValue)
|
||
#pragma alloc_text(PAGE, ArbInitializeOrderingList)
|
||
#pragma alloc_text(PAGE, ArbCopyOrderingList)
|
||
#pragma alloc_text(PAGE, ArbAddOrdering)
|
||
#pragma alloc_text(PAGE, ArbPruneOrdering)
|
||
#pragma alloc_text(PAGE, ArbFreeOrderingList)
|
||
#pragma alloc_text(PAGE, ArbOverrideConflict)
|
||
#pragma alloc_text(PAGE, ArbpUpdatePriority)
|
||
#pragma alloc_text(PAGE, ArbAddReserved)
|
||
#pragma alloc_text(PAGE, ArbpQueryConflictCallback)
|
||
#pragma alloc_text(PAGE, ArbQueryConflict)
|
||
#pragma alloc_text(PAGE, ArbStartArbiter)
|
||
#pragma alloc_text(PAGE, ArbShareDriverExclusive)
|
||
|
||
#endif // ALLOC_PRAGMA
|
||
|
||
//
|
||
// Implementation
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
ArbInitializeArbiterInstance(
|
||
OUT PARBITER_INSTANCE Arbiter,
|
||
IN PDEVICE_OBJECT BusDeviceObject,
|
||
IN CM_RESOURCE_TYPE ResourceType,
|
||
IN PWSTR Name,
|
||
IN PWSTR OrderingName,
|
||
IN PARBITER_TRANSLATE_ALLOCATION_ORDER TranslateOrdering OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes an arbiter instance and fills in any optional NULL
|
||
dispatch table entries with system default functions.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - A caller allocated arbiter instance structure.
|
||
The UnpackRequirement, PackResource, UnpackResource and ScoreRequirement
|
||
entries should be initialized with the appropriate routines as should
|
||
any other entries if the default system routines are not sufficient.
|
||
|
||
BusDeviceObject - The device object that exposes this arbiter - normally an
|
||
FDO.
|
||
|
||
ResourceType - The resource type this arbiter arbitrates.
|
||
|
||
Name - A string used to identify the arbiter, used in debug messages and
|
||
for registry storage
|
||
|
||
OrderingName - The name of the preferred assignment ordering list under
|
||
HKLM\System\CurrentControlSet\Control\SystemResources\AssignmentOrdering
|
||
|
||
|
||
TranslateOrdering - Function that, if present, will be called to translate
|
||
each descriptor from the ordering list
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(Arbiter->UnpackRequirement);
|
||
ASSERT(Arbiter->PackResource);
|
||
ASSERT(Arbiter->UnpackResource);
|
||
|
||
ARB_PRINT(2,("Initializing %S Arbiter...\n", Name));
|
||
|
||
//
|
||
// Initialize all pool allocation pointers to NULL so we can cleanup
|
||
//
|
||
|
||
ASSERT(Arbiter->MutexEvent == NULL
|
||
&& Arbiter->Allocation == NULL
|
||
&& Arbiter->PossibleAllocation == NULL
|
||
&& Arbiter->AllocationStack == NULL
|
||
);
|
||
|
||
//
|
||
// We are an arbiter
|
||
//
|
||
|
||
Arbiter->Signature = ARBITER_INSTANCE_SIGNATURE;
|
||
|
||
//
|
||
// Remember the bus that produced us
|
||
//
|
||
|
||
Arbiter->BusDeviceObject = BusDeviceObject;
|
||
|
||
//
|
||
// Initialize state lock (KEVENT must be non-paged)
|
||
//
|
||
|
||
Arbiter->MutexEvent = ExAllocatePoolWithTag(NonPagedPool,
|
||
sizeof(KEVENT),
|
||
ARBITER_MISC_TAG
|
||
);
|
||
|
||
if (!Arbiter->MutexEvent) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
KeInitializeEvent(Arbiter->MutexEvent, SynchronizationEvent, TRUE);
|
||
|
||
//
|
||
// Initialize the allocation stack to a reasonable size
|
||
//
|
||
|
||
Arbiter->AllocationStack = ExAllocatePoolWithTag(PagedPool,
|
||
INITIAL_ALLOCATION_STATE_SIZE,
|
||
ARBITER_ALLOCATION_STATE_TAG
|
||
);
|
||
|
||
if (!Arbiter->AllocationStack) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
Arbiter->AllocationStackMaxSize = INITIAL_ALLOCATION_STATE_SIZE;
|
||
|
||
|
||
//
|
||
// Allocate buffers to hold the range lists
|
||
//
|
||
|
||
Arbiter->Allocation = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(RTL_RANGE_LIST),
|
||
ARBITER_RANGE_LIST_TAG
|
||
);
|
||
|
||
if (!Arbiter->Allocation) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
Arbiter->PossibleAllocation = ExAllocatePoolWithTag(PagedPool,
|
||
sizeof(RTL_RANGE_LIST),
|
||
ARBITER_RANGE_LIST_TAG
|
||
);
|
||
|
||
if (!Arbiter->PossibleAllocation) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Initialize the range lists
|
||
//
|
||
|
||
RtlInitializeRangeList(Arbiter->Allocation);
|
||
RtlInitializeRangeList(Arbiter->PossibleAllocation);
|
||
|
||
//
|
||
// Initialize the data fields
|
||
//
|
||
Arbiter->TransactionInProgress = FALSE;
|
||
Arbiter->Name = Name;
|
||
Arbiter->ResourceType = ResourceType;
|
||
|
||
//
|
||
// If the caller has not supplied the optional functions set them to the
|
||
// defaults (If this were C++ we'd just inherit this loit but seeing as its
|
||
// not we'll do it the old fashioned way...)
|
||
//
|
||
|
||
if (!Arbiter->TestAllocation) {
|
||
Arbiter->TestAllocation = ArbTestAllocation;
|
||
}
|
||
|
||
if (!Arbiter->RetestAllocation) {
|
||
Arbiter->RetestAllocation = ArbRetestAllocation;
|
||
}
|
||
|
||
if (!Arbiter->CommitAllocation) {
|
||
Arbiter->CommitAllocation = ArbCommitAllocation;
|
||
}
|
||
|
||
if (!Arbiter->RollbackAllocation) {
|
||
Arbiter->RollbackAllocation = ArbRollbackAllocation;
|
||
}
|
||
|
||
if (!Arbiter->AddReserved) {
|
||
Arbiter->AddReserved = ArbAddReserved;
|
||
}
|
||
|
||
if (!Arbiter->PreprocessEntry) {
|
||
Arbiter->PreprocessEntry = ArbPreprocessEntry;
|
||
}
|
||
|
||
if (!Arbiter->AllocateEntry) {
|
||
Arbiter->AllocateEntry = ArbAllocateEntry;
|
||
}
|
||
|
||
if (!Arbiter->GetNextAllocationRange) {
|
||
Arbiter->GetNextAllocationRange = ArbGetNextAllocationRange;
|
||
}
|
||
|
||
if (!Arbiter->FindSuitableRange) {
|
||
Arbiter->FindSuitableRange = ArbFindSuitableRange;
|
||
}
|
||
|
||
if (!Arbiter->AddAllocation) {
|
||
Arbiter->AddAllocation = ArbAddAllocation;
|
||
}
|
||
|
||
if (!Arbiter->BacktrackAllocation) {
|
||
Arbiter->BacktrackAllocation = ArbBacktrackAllocation;
|
||
}
|
||
|
||
if (!Arbiter->OverrideConflict) {
|
||
Arbiter->OverrideConflict = ArbOverrideConflict;
|
||
}
|
||
|
||
if (!Arbiter->BootAllocation) {
|
||
Arbiter->BootAllocation = ArbBootAllocation;
|
||
}
|
||
|
||
if (!Arbiter->QueryConflict) {
|
||
Arbiter->QueryConflict = ArbQueryConflict;
|
||
}
|
||
|
||
if (!Arbiter->StartArbiter) {
|
||
Arbiter->StartArbiter = ArbStartArbiter;
|
||
}
|
||
|
||
//
|
||
// Build the prefered assignment ordering - we assume that the reserved
|
||
// ranges have the same name as the assignment ordering
|
||
//
|
||
|
||
status = ArbBuildAssignmentOrdering(Arbiter,
|
||
OrderingName,
|
||
OrderingName,
|
||
TranslateOrdering
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
if (Arbiter->MutexEvent) {
|
||
ExFreePool(Arbiter->MutexEvent);
|
||
}
|
||
|
||
if (Arbiter->Allocation) {
|
||
ExFreePool(Arbiter->Allocation);
|
||
}
|
||
|
||
if (Arbiter->PossibleAllocation) {
|
||
ExFreePool(Arbiter->PossibleAllocation);
|
||
}
|
||
|
||
if (Arbiter->AllocationStack) {
|
||
ExFreePool(Arbiter->AllocationStack);
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
VOID
|
||
ArbReferenceArbiterInstance(
|
||
IN PARBITER_INSTANCE Arbiter
|
||
)
|
||
{
|
||
InterlockedIncrement(&Arbiter->ReferenceCount);
|
||
}
|
||
|
||
VOID
|
||
ArbDereferenceArbiterInstance(
|
||
IN PARBITER_INSTANCE Arbiter
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
InterlockedDecrement(&Arbiter->ReferenceCount);
|
||
|
||
if (Arbiter->ReferenceCount == 0) {
|
||
ArbDeleteArbiterInstance(Arbiter);
|
||
}
|
||
}
|
||
|
||
VOID
|
||
ArbDeleteArbiterInstance(
|
||
IN PARBITER_INSTANCE Arbiter
|
||
)
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
if (Arbiter->MutexEvent) {
|
||
ExFreePool(Arbiter->MutexEvent);
|
||
}
|
||
|
||
if (Arbiter->Allocation) {
|
||
RtlFreeRangeList(Arbiter->Allocation);
|
||
ExFreePool(Arbiter->Allocation);
|
||
}
|
||
|
||
if (Arbiter->PossibleAllocation) {
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
ExFreePool(Arbiter->PossibleAllocation);
|
||
}
|
||
|
||
if (Arbiter->AllocationStack) {
|
||
ExFreePool(Arbiter->AllocationStack);
|
||
}
|
||
|
||
ArbFreeOrderingList(&Arbiter->OrderingList);
|
||
ArbFreeOrderingList(&Arbiter->ReservedList);
|
||
|
||
#if ARB_DBG
|
||
|
||
RtlFillMemory(Arbiter, sizeof(ARBITER_INSTANCE), 'A');
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbTestAllocation(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN OUT PLIST_ENTRY ArbitrationList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the default implementation of the arbiter Test Allocation action.
|
||
It takes a list of requests for resources for particular devices and attempts
|
||
to satisfy them.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The instance of the arbiter being called.
|
||
|
||
ArbitrationList - A list of ARBITER_LIST_ENTRY entries which contain the
|
||
requirements and associated devices.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
These include:
|
||
|
||
STATUS_SUCCESSFUL - Arbitration suceeded and an allocation has been made for
|
||
all the entries in the arbitration list.
|
||
|
||
STATUS_UNSUCCESSFUL - Arbitration failed to find an allocation for all
|
||
entries.
|
||
|
||
STATUS_ARBITRATION_UNHANDLED - If returning this error the arbiter is
|
||
partial (and therefore must have set the ARBITER_PARTIAL flag in its
|
||
interface.) This status indicates that this arbiter doesn't handle the
|
||
resources requested and the next arbiter towards the root of the device
|
||
tree should be asked instead.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PARBITER_LIST_ENTRY current;
|
||
PIO_RESOURCE_DESCRIPTOR alternative;
|
||
ULONG count;
|
||
PDEVICE_OBJECT previousOwner;
|
||
PDEVICE_OBJECT currentOwner;
|
||
LONG score;
|
||
BOOLEAN performScoring;
|
||
|
||
PAGED_CODE();
|
||
ASSERT(Arbiter);
|
||
|
||
//
|
||
// Copy the current allocation
|
||
//
|
||
|
||
ARB_PRINT(3, ("Copy current allocation\n"));
|
||
status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Free all the resources currently allocated to all the devices we
|
||
// are arbitrating for
|
||
//
|
||
|
||
count = 0;
|
||
previousOwner = NULL;
|
||
|
||
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, current) {
|
||
|
||
count++;
|
||
|
||
currentOwner = current->PhysicalDeviceObject;
|
||
|
||
if (previousOwner != currentOwner) {
|
||
|
||
previousOwner = currentOwner;
|
||
|
||
ARB_PRINT(3,
|
||
("Delete 0x%08x's resources\n",
|
||
currentOwner
|
||
));
|
||
|
||
status = RtlDeleteOwnersRanges(Arbiter->PossibleAllocation,
|
||
(PVOID)currentOwner
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Score the entries in the arbitration list if a scoring function was
|
||
// provided and this is not a legacy request (which is guaranteed to be
|
||
// made of all fixed requests so sorting is pointless)
|
||
//
|
||
|
||
performScoring = Arbiter->ScoreRequirement != NULL;
|
||
//
|
||
// ISSUE-2000/03/06-andrewth
|
||
// Ensure that in the start and enum cleanup the RequestSource is correctly passed in
|
||
// so we can safely skip the unnecesary scoring and sorting
|
||
// && !LEGACY_REQUEST(current);
|
||
//
|
||
current->WorkSpace = 0;
|
||
|
||
if (performScoring) {
|
||
|
||
FOR_ALL_IN_ARRAY(current->Alternatives,
|
||
current->AlternativeCount,
|
||
alternative) {
|
||
|
||
ARB_PRINT(3,
|
||
("Scoring entry %p\n",
|
||
currentOwner
|
||
));
|
||
|
||
|
||
|
||
score = Arbiter->ScoreRequirement(alternative);
|
||
|
||
//
|
||
// Ensure the score is valid
|
||
//
|
||
|
||
if (score < 0) {
|
||
status = STATUS_DEVICE_CONFIGURATION_ERROR;
|
||
goto cleanup;
|
||
}
|
||
|
||
current->WorkSpace += score;
|
||
}
|
||
}
|
||
}
|
||
|
||
status = ArbSortArbitrationList(ArbitrationList);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Build the arbitration stack
|
||
//
|
||
|
||
status = ArbpBuildAllocationStack(Arbiter,
|
||
ArbitrationList,
|
||
count
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Attempt allocation
|
||
//
|
||
|
||
status = Arbiter->AllocateEntry(Arbiter, Arbiter->AllocationStack);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// Success.
|
||
//
|
||
|
||
return status;
|
||
}
|
||
|
||
cleanup:
|
||
|
||
//
|
||
// We didn't succeed so empty the possible allocation list...
|
||
//
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbpBuildAlternative(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PIO_RESOURCE_DESCRIPTOR Requirement,
|
||
OUT PARBITER_ALTERNATIVE Alternative
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes a arbiter alternative from a given resource
|
||
requirement descriptor
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The arbiter instance data where the allocation stack should be
|
||
placed.
|
||
|
||
Requirement - The requirement descriptor describing this requirement
|
||
|
||
Alternative - The alternative to be initialized
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
ASSERT(Alternative && Requirement);
|
||
|
||
Alternative->Descriptor = Requirement;
|
||
|
||
//
|
||
// Unpack the requirement into the alternatives table
|
||
//
|
||
|
||
status = Arbiter->UnpackRequirement(Requirement,
|
||
&Alternative->Minimum,
|
||
&Alternative->Maximum,
|
||
&Alternative->Length,
|
||
&Alternative->Alignment
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Align the minimum if necessary
|
||
//
|
||
|
||
if (Alternative->Minimum % Alternative->Alignment != 0) {
|
||
ALIGN_ADDRESS_UP(Alternative->Minimum,
|
||
Alternative->Alignment
|
||
);
|
||
}
|
||
|
||
Alternative->Flags = 0;
|
||
|
||
//
|
||
// Check if this alternative is shared
|
||
//
|
||
|
||
if(Requirement->ShareDisposition == CmResourceShareShared) {
|
||
Alternative->Flags |= ARBITER_ALTERNATIVE_FLAG_SHARED;
|
||
}
|
||
|
||
//
|
||
// Check if this alternative is fixed
|
||
//
|
||
|
||
if (Alternative->Maximum - Alternative->Minimum + 1 == Alternative->Length) {
|
||
Alternative->Flags |= ARBITER_ALTERNATIVE_FLAG_FIXED;
|
||
}
|
||
|
||
//
|
||
// Check for validity
|
||
//
|
||
|
||
if (Alternative->Maximum < Alternative->Minimum) {
|
||
Alternative->Flags |= ARBITER_ALTERNATIVE_FLAG_INVALID;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbpBuildAllocationStack(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PLIST_ENTRY ArbitrationList,
|
||
IN ULONG ArbitrationListCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the allocation stack for the requests in
|
||
ArbitrationList. It overwrites any previous allocation stack and allocates
|
||
additional memory if more is required. Arbiter->AllocationStack contains
|
||
the initialized stack on success.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The arbiter instance data where the allocation stack should be
|
||
placed.
|
||
|
||
ArbitrationList - A list of ARBITER_LIST_ENTRY entries which contain the
|
||
requirements and associated devices.
|
||
|
||
ArbitrationListCount - The number of entries in the ArbitrationList
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PARBITER_LIST_ENTRY currentEntry;
|
||
PARBITER_ALLOCATION_STATE currentState;
|
||
ULONG stackSize = 0, allocationCount = ArbitrationListCount + 1;
|
||
PARBITER_ALTERNATIVE currentAlternative;
|
||
PIO_RESOURCE_DESCRIPTOR currentDescriptor;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Calculate the size the stack needs to be and the
|
||
//
|
||
|
||
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, currentEntry) {
|
||
|
||
if (currentEntry->AlternativeCount > 0) {
|
||
stackSize += currentEntry->AlternativeCount
|
||
* sizeof(ARBITER_ALTERNATIVE);
|
||
} else {
|
||
allocationCount--;
|
||
}
|
||
}
|
||
|
||
stackSize += allocationCount * sizeof(ARBITER_ALLOCATION_STATE);
|
||
|
||
//
|
||
// Make sure the allocation stack is large enough
|
||
//
|
||
|
||
if (Arbiter->AllocationStackMaxSize < stackSize) {
|
||
|
||
PARBITER_ALLOCATION_STATE temp;
|
||
|
||
//
|
||
// Enlarge the allocation stack
|
||
//
|
||
|
||
temp = ExAllocatePoolWithTag(PagedPool,
|
||
stackSize,
|
||
ARBITER_ALLOCATION_STATE_TAG
|
||
);
|
||
if (!temp) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
ExFreePool(Arbiter->AllocationStack);
|
||
Arbiter->AllocationStack = temp;
|
||
}
|
||
|
||
RtlZeroMemory(Arbiter->AllocationStack, stackSize);
|
||
|
||
//
|
||
// Fill in the locations
|
||
//
|
||
|
||
currentState = Arbiter->AllocationStack;
|
||
currentAlternative = (PARBITER_ALTERNATIVE) (Arbiter->AllocationStack
|
||
+ ArbitrationListCount + 1);
|
||
|
||
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, currentEntry) {
|
||
|
||
//
|
||
// Do we need to allocate anything for this entry?
|
||
//
|
||
|
||
if (currentEntry->AlternativeCount > 0) {
|
||
|
||
//
|
||
// Initialize the stack location
|
||
//
|
||
|
||
currentState->Entry = currentEntry;
|
||
currentState->AlternativeCount = currentEntry->AlternativeCount;
|
||
currentState->Alternatives = currentAlternative;
|
||
|
||
//
|
||
// Initialize the start and end values to an invalid range so
|
||
// that we don't skip the range 0-0 every time...
|
||
//
|
||
|
||
currentState->Start = 1;
|
||
ASSERT(currentState->End == 0); // From RtlZeroMemory
|
||
|
||
//
|
||
// Initialize the alternatives table
|
||
//
|
||
|
||
FOR_ALL_IN_ARRAY(currentEntry->Alternatives,
|
||
currentEntry->AlternativeCount,
|
||
currentDescriptor) {
|
||
|
||
|
||
status = ArbpBuildAlternative(Arbiter,
|
||
currentDescriptor,
|
||
currentAlternative
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Initialize the priority
|
||
//
|
||
|
||
currentAlternative->Priority = ARBITER_PRIORITY_NULL;
|
||
|
||
//
|
||
// Advance to the next alternative
|
||
//
|
||
|
||
currentAlternative++;
|
||
|
||
}
|
||
}
|
||
currentState++;
|
||
}
|
||
|
||
//
|
||
// Terminate the stack with NULL entry
|
||
//
|
||
|
||
currentState->Entry = NULL;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
//
|
||
// We don't need to free the buffer as it is attached to the arbiter and
|
||
// will be used next time
|
||
//
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbSortArbitrationList(
|
||
IN OUT PLIST_ENTRY ArbitrationList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sorts the arbitration list in order of each entry's
|
||
WorkSpace value.
|
||
|
||
Parameters:
|
||
|
||
ArbitrationList - The list to be sorted.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN sorted = FALSE;
|
||
PARBITER_LIST_ENTRY current, next;
|
||
|
||
PAGED_CODE();
|
||
|
||
ARB_PRINT(3, ("IoSortArbiterList(%p)\n", ArbitrationList));
|
||
|
||
while (!sorted) {
|
||
|
||
sorted = TRUE;
|
||
|
||
for (current=(PARBITER_LIST_ENTRY) ArbitrationList->Flink,
|
||
next=(PARBITER_LIST_ENTRY) current->ListEntry.Flink;
|
||
|
||
(PLIST_ENTRY) current != ArbitrationList
|
||
&& (PLIST_ENTRY) next != ArbitrationList;
|
||
|
||
current = (PARBITER_LIST_ENTRY) current->ListEntry.Flink,
|
||
next = (PARBITER_LIST_ENTRY)current->ListEntry.Flink) {
|
||
|
||
|
||
if (current->WorkSpace > next->WorkSpace) {
|
||
|
||
PLIST_ENTRY before = current->ListEntry.Blink;
|
||
PLIST_ENTRY after = next->ListEntry.Flink;
|
||
|
||
//
|
||
// Swap the locations of current and next
|
||
//
|
||
|
||
before->Flink = (PLIST_ENTRY) next;
|
||
after->Blink = (PLIST_ENTRY) current;
|
||
current->ListEntry.Flink = after;
|
||
current->ListEntry.Blink = (PLIST_ENTRY) next;
|
||
next->ListEntry.Flink = (PLIST_ENTRY) current;
|
||
next->ListEntry.Blink = before;
|
||
|
||
sorted = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbCommitAllocation(
|
||
PARBITER_INSTANCE Arbiter
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This provides the default implementation of the CommitAllocation action.
|
||
It frees the old allocation and replaces it with the new allocation.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The arbiter instance data for the arbiter being called.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRTL_RANGE_LIST temp;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Free up the current allocation
|
||
//
|
||
|
||
RtlFreeRangeList(Arbiter->Allocation);
|
||
|
||
//
|
||
// Swap the allocated and duplicate lists
|
||
//
|
||
|
||
temp = Arbiter->Allocation;
|
||
Arbiter->Allocation = Arbiter->PossibleAllocation;
|
||
Arbiter->PossibleAllocation = temp;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbRollbackAllocation(
|
||
IN PARBITER_INSTANCE Arbiter
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This provides the default implementation of the RollbackAllocation action.
|
||
It frees the possible allocation the last TestAllocation provided.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The arbiter instance data for the arbiter being called.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Free up the possible allocation
|
||
//
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbRetestAllocation(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN OUT PLIST_ENTRY ArbitrationList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This provides the default implementation of the RetestAllocation action.
|
||
It walks the arbitration list and updates the possible allocation to reflect
|
||
the allocation entries of the list. For these entries to be valid
|
||
TestAllocation must have been performed on this arbitration list.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The arbiter instance data for the arbiter being called.
|
||
|
||
ArbitrationList - A list of ARBITER_LIST_ENTRY entries which contain the
|
||
requirements and associated devices. TestAllocation for this arbiter
|
||
should have been called on this list.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PARBITER_LIST_ENTRY current;
|
||
ARBITER_ALLOCATION_STATE state;
|
||
ARBITER_ALTERNATIVE alternative;
|
||
ULONG length;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize the state
|
||
//
|
||
|
||
RtlZeroMemory(&state, sizeof(ARBITER_ALLOCATION_STATE));
|
||
RtlZeroMemory(&alternative, sizeof(ARBITER_ALTERNATIVE));
|
||
state.AlternativeCount = 1;
|
||
state.Alternatives = &alternative;
|
||
state.CurrentAlternative = &alternative;
|
||
state.Flags = ARBITER_STATE_FLAG_RETEST;
|
||
|
||
//
|
||
// Copy the current allocation and reserved
|
||
//
|
||
|
||
ARB_PRINT(2, ("Retest: Copy current allocation\n"));
|
||
status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Free all the resources currently allocated to all the devices we
|
||
// are arbitrating for
|
||
//
|
||
|
||
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, current) {
|
||
|
||
ARB_PRINT(3,
|
||
("Retest: Delete 0x%08x's resources\n",
|
||
current->PhysicalDeviceObject
|
||
));
|
||
|
||
status = RtlDeleteOwnersRanges(Arbiter->PossibleAllocation,
|
||
(PVOID) current->PhysicalDeviceObject
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Build an allocation state for the allocation and call AddAllocation to
|
||
// update the range lists accordingly
|
||
//
|
||
|
||
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, current) {
|
||
|
||
ASSERT(current->Assignment && current->SelectedAlternative);
|
||
|
||
state.WorkSpace = 0;
|
||
state.Entry = current;
|
||
|
||
//
|
||
// Initialize the alternative
|
||
//
|
||
|
||
status = ArbpBuildAlternative(Arbiter,
|
||
current->SelectedAlternative,
|
||
&alternative
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
//
|
||
// Update it with our allocation
|
||
//
|
||
|
||
status = Arbiter->UnpackResource(current->Assignment,
|
||
&state.Start,
|
||
&length
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
state.End = state.Start + length - 1;
|
||
|
||
//
|
||
// Do any preprocessing that is required
|
||
//
|
||
|
||
status = Arbiter->PreprocessEntry(Arbiter,&state);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// If we had a requirement for length 0 then don't attemp to add the
|
||
// range - it will fail!
|
||
//
|
||
|
||
if (length != 0) {
|
||
|
||
Arbiter->AddAllocation(Arbiter, &state);
|
||
|
||
}
|
||
}
|
||
|
||
return status;
|
||
|
||
cleanup:
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbBootAllocation(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN OUT PLIST_ENTRY ArbitrationList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This provides the default implementation of the BootAllocation action.
|
||
It walks the arbitration list and updates the allocation to reflect the fact
|
||
that the allocation entries in the list are in use.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The arbiter instance data for the arbiter being called.
|
||
|
||
ArbitrationList - A list of ARBITER_LIST_ENTRY entries which contain the
|
||
requirements and associated devices. Each device should have one and
|
||
only one requirement reflecting the resources it is currently consuming.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PARBITER_LIST_ENTRY current;
|
||
PRTL_RANGE_LIST temp;
|
||
ARBITER_ALLOCATION_STATE state;
|
||
ARBITER_ALTERNATIVE alternative;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize the state
|
||
//
|
||
|
||
RtlZeroMemory(&state, sizeof(ARBITER_ALLOCATION_STATE));
|
||
RtlZeroMemory(&alternative, sizeof(ARBITER_ALTERNATIVE));
|
||
state.AlternativeCount = 1;
|
||
state.Alternatives = &alternative;
|
||
state.CurrentAlternative = &alternative;
|
||
state.Flags = ARBITER_STATE_FLAG_BOOT;
|
||
state.RangeAttributes = ARBITER_RANGE_BOOT_ALLOCATED;
|
||
|
||
//
|
||
// Work on the possible allocation list
|
||
//
|
||
|
||
status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
|
||
|
||
FOR_ALL_IN_LIST(ARBITER_LIST_ENTRY, ArbitrationList, current) {
|
||
|
||
ASSERT(current->AlternativeCount == 1);
|
||
ASSERT(current->PhysicalDeviceObject);
|
||
|
||
//
|
||
// Build an alternative and state structure for this allocation and
|
||
// add it to the range list
|
||
//
|
||
|
||
state.Entry = current;
|
||
|
||
//
|
||
// Initialize the alternative
|
||
//
|
||
|
||
status = ArbpBuildAlternative(Arbiter,
|
||
¤t->Alternatives[0],
|
||
&alternative
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
ASSERT(alternative.Flags &
|
||
(ARBITER_ALTERNATIVE_FLAG_FIXED | ARBITER_ALTERNATIVE_FLAG_INVALID)
|
||
);
|
||
|
||
state.Start = alternative.Minimum;
|
||
state.End = alternative.Maximum;
|
||
|
||
//
|
||
// Blow away the old workspace and masks
|
||
//
|
||
|
||
state.WorkSpace = 0;
|
||
state.RangeAvailableAttributes = 0;
|
||
|
||
//
|
||
// Validate the requirement
|
||
//
|
||
|
||
if (alternative.Length == 0
|
||
|| alternative.Alignment == 0
|
||
|| state.End < state.Start
|
||
|| state.Start % alternative.Alignment != 0
|
||
|| LENGTH_OF(state.Start, state.End) != alternative.Length) {
|
||
|
||
ARB_PRINT(1,
|
||
("Skipping invalid boot allocation 0x%I64x-0x%I64x L 0x%x A 0x%x for 0x%08x\n",
|
||
state.Start,
|
||
state.End,
|
||
alternative.Length,
|
||
alternative.Alignment,
|
||
current->PhysicalDeviceObject
|
||
));
|
||
|
||
continue;
|
||
}
|
||
|
||
#if PLUG_FEST_HACKS
|
||
|
||
if (alternative.Flags & ARBITER_ALTERNATIVE_FLAG_SHARED) {
|
||
|
||
ARB_PRINT(1,
|
||
("Skipping shared boot allocation 0x%I64x-0x%I64x L 0x%x A 0x%x for 0x%08x\n",
|
||
state.Start,
|
||
state.End,
|
||
alternative.Length,
|
||
alternative.Alignment,
|
||
current->PhysicalDeviceObject
|
||
));
|
||
|
||
continue;
|
||
}
|
||
#endif
|
||
|
||
|
||
//
|
||
// Do any preprocessing that is required
|
||
//
|
||
|
||
status = Arbiter->PreprocessEntry(Arbiter,&state);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;;
|
||
}
|
||
|
||
Arbiter->AddAllocation(Arbiter, &state);
|
||
|
||
}
|
||
|
||
//
|
||
// Everything went OK so make this our allocated range
|
||
//
|
||
|
||
RtlFreeRangeList(Arbiter->Allocation);
|
||
temp = Arbiter->Allocation;
|
||
Arbiter->Allocation = Arbiter->PossibleAllocation;
|
||
Arbiter->PossibleAllocation = temp;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
return status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbArbiterHandler(
|
||
IN PVOID Context,
|
||
IN ARBITER_ACTION Action,
|
||
IN OUT PARBITER_PARAMETERS Params
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This provides the default entry point to an arbiter.
|
||
|
||
Parameters:
|
||
|
||
Context - The context provided in the interface where this function was
|
||
called from. This is converted to an ARBITER_INSTANCE using the
|
||
ARBITER_CONTEXT_TO_INSTANCE macro which should be defined.
|
||
|
||
Action - The action the arbiter should perform.
|
||
|
||
Params - The parameters for the action.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
Note:
|
||
|
||
The routines which implement each action are determined from the dispatch
|
||
table in the arbiter instance.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PARBITER_INSTANCE arbiter = Context;
|
||
|
||
PAGED_CODE();
|
||
ASSERT(Context);
|
||
ASSERT(Action >= 0 && Action <= ArbiterActionBootAllocation);
|
||
ASSERT(arbiter->Signature == ARBITER_INSTANCE_SIGNATURE);
|
||
|
||
//
|
||
// Acquire the state lock
|
||
//
|
||
|
||
ArbAcquireArbiterLock(arbiter);
|
||
|
||
//
|
||
// Announce ourselves
|
||
//
|
||
|
||
ARB_PRINT(2,
|
||
("%s %S\n",
|
||
ArbpActionStrings[Action],
|
||
arbiter->Name
|
||
));
|
||
|
||
//
|
||
// Check the transaction flag
|
||
//
|
||
|
||
if (Action == ArbiterActionTestAllocation
|
||
|| Action == ArbiterActionRetestAllocation
|
||
|| Action == ArbiterActionBootAllocation) {
|
||
|
||
ASSERT(!arbiter->TransactionInProgress);
|
||
|
||
} else if (Action == ArbiterActionCommitAllocation
|
||
|| Action == ArbiterActionRollbackAllocation) {
|
||
|
||
ASSERT(arbiter->TransactionInProgress);
|
||
}
|
||
|
||
#if ARB_DBG
|
||
|
||
replay:
|
||
|
||
#endif
|
||
|
||
//
|
||
// Do the appropriate thing
|
||
//
|
||
|
||
switch (Action) {
|
||
|
||
case ArbiterActionTestAllocation:
|
||
|
||
//
|
||
// NTRAID #95564-2000/02/31-andrewth
|
||
// Until we support rebalance we don't deal with AllocateFrom
|
||
//
|
||
|
||
ASSERT(Params->Parameters.TestAllocation.AllocateFromCount == 0);
|
||
ASSERT(Params->Parameters.TestAllocation.AllocateFrom == NULL);
|
||
|
||
status = arbiter->TestAllocation(
|
||
arbiter,
|
||
Params->Parameters.TestAllocation.ArbitrationList
|
||
);
|
||
break;
|
||
|
||
case ArbiterActionRetestAllocation:
|
||
|
||
ASSERT(Params->Parameters.TestAllocation.AllocateFromCount == 0);
|
||
ASSERT(Params->Parameters.TestAllocation.AllocateFrom == NULL);
|
||
|
||
status = arbiter->RetestAllocation(
|
||
arbiter,
|
||
Params->Parameters.TestAllocation.ArbitrationList
|
||
);
|
||
break;
|
||
|
||
case ArbiterActionCommitAllocation:
|
||
|
||
status = arbiter->CommitAllocation(arbiter);
|
||
|
||
break;
|
||
|
||
case ArbiterActionRollbackAllocation:
|
||
|
||
status = arbiter->RollbackAllocation(arbiter);
|
||
|
||
break;
|
||
|
||
case ArbiterActionBootAllocation:
|
||
|
||
status = arbiter->BootAllocation(
|
||
arbiter,
|
||
Params->Parameters.BootAllocation.ArbitrationList
|
||
);
|
||
break;
|
||
|
||
case ArbiterActionQueryConflict:
|
||
|
||
status = arbiter->QueryConflict(
|
||
arbiter,
|
||
Params->Parameters.QueryConflict.PhysicalDeviceObject,
|
||
Params->Parameters.QueryConflict.ConflictingResource,
|
||
Params->Parameters.QueryConflict.ConflictCount,
|
||
Params->Parameters.QueryConflict.Conflicts
|
||
);
|
||
break;
|
||
|
||
case ArbiterActionQueryArbitrate:
|
||
case ArbiterActionQueryAllocatedResources:
|
||
case ArbiterActionWriteReservedResources:
|
||
case ArbiterActionAddReserved:
|
||
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
break;
|
||
|
||
default:
|
||
status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
|
||
#if ARB_DBG
|
||
|
||
//
|
||
// Check if we failed and want to stop or replay on errors
|
||
//
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
|
||
ARB_PRINT(1,
|
||
("*** %s for %S FAILED status = %08x\n",
|
||
ArbpActionStrings[Action],
|
||
arbiter->Name,
|
||
status
|
||
));
|
||
|
||
if (ArbStopOnError) {
|
||
DbgBreakPoint();
|
||
}
|
||
|
||
if (ArbReplayOnError) {
|
||
goto replay;
|
||
}
|
||
}
|
||
|
||
#endif // ARB_DBG
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
if (Action == ArbiterActionTestAllocation
|
||
|| Action == ArbiterActionRetestAllocation) {
|
||
|
||
arbiter->TransactionInProgress = TRUE;
|
||
|
||
} else if (Action == ArbiterActionCommitAllocation
|
||
|| Action == ArbiterActionRollbackAllocation) {
|
||
|
||
arbiter->TransactionInProgress = FALSE;
|
||
}
|
||
}
|
||
|
||
ArbReleaseArbiterLock(arbiter);
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbBuildAssignmentOrdering(
|
||
IN OUT PARBITER_INSTANCE Arbiter,
|
||
IN PWSTR AllocationOrderName,
|
||
IN PWSTR ReservedResourcesName,
|
||
IN PARBITER_TRANSLATE_ALLOCATION_ORDER Translate OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is called as part of arbiter initialization and extracts the allocation
|
||
ordering and reserved information from the registry and combines them into
|
||
an ordering list. The reserved ranges are put in Arbiter->ReservedList
|
||
and the initial ordering in Arbiter->OrderingList.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The instance data of the arbiter to be initialized.
|
||
|
||
AllocationOrderName - The name of the key under HKLM\System\
|
||
CurrentControlSet\Control\Arbiters\AllocationOrder the ordering
|
||
information should be taken from.
|
||
|
||
ReservedResourcesName - The name of the key under HKLM\System\
|
||
CurrentControlSet\Control\Arbiters\ReservedResources the reserved ranges
|
||
information should be taken from.
|
||
|
||
Translate - A function to be called for each range that will perform system
|
||
dependant translations required for this system.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE arbitersHandle = NULL, tempHandle = NULL;
|
||
UNICODE_STRING unicodeString;
|
||
PKEY_VALUE_FULL_INFORMATION info = NULL;
|
||
ULONG dummy;
|
||
PIO_RESOURCE_LIST resourceList;
|
||
PIO_RESOURCE_DESCRIPTOR current;
|
||
ULONGLONG start, end;
|
||
OBJECT_ATTRIBUTES attributes;
|
||
IO_RESOURCE_DESCRIPTOR translated;
|
||
|
||
PAGED_CODE();
|
||
|
||
ArbAcquireArbiterLock(Arbiter);
|
||
|
||
//
|
||
// If we are reinitializing the orderings free the old ones
|
||
//
|
||
|
||
ArbFreeOrderingList(&Arbiter->OrderingList);
|
||
ArbFreeOrderingList(&Arbiter->ReservedList);
|
||
|
||
//
|
||
// Initialize the orderings
|
||
//
|
||
|
||
status = ArbInitializeOrderingList(&Arbiter->OrderingList);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
status = ArbInitializeOrderingList(&Arbiter->ReservedList);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Open HKLM\System\CurrentControlSet\Control\Arbiters
|
||
//
|
||
|
||
ArbpWstrToUnicodeString(&unicodeString, PATH_ARBITERS);
|
||
InitializeObjectAttributes(&attributes,
|
||
&unicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
(PSECURITY_DESCRIPTOR) NULL
|
||
);
|
||
|
||
|
||
status = ZwOpenKey(&arbitersHandle,
|
||
KEY_READ,
|
||
&attributes
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Open AllocationOrder
|
||
//
|
||
|
||
ArbpWstrToUnicodeString(&unicodeString, KEY_ALLOCATIONORDER);
|
||
InitializeObjectAttributes(&attributes,
|
||
&unicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
arbitersHandle,
|
||
(PSECURITY_DESCRIPTOR) NULL
|
||
);
|
||
|
||
|
||
status = ZwOpenKey(&tempHandle,
|
||
KEY_READ,
|
||
&attributes
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Extract the value the user asked for
|
||
//
|
||
|
||
status = ArbpGetRegistryValue(tempHandle,
|
||
AllocationOrderName,
|
||
&info
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Check if the value we retrieved was a string and if so then it was a
|
||
// short cut to a value of that name - open it.
|
||
//
|
||
|
||
if (info->Type == REG_SZ) {
|
||
|
||
PKEY_VALUE_FULL_INFORMATION tempInfo;
|
||
PWSTR shortcut = (PWSTR) FULL_INFO_DATA(info);
|
||
|
||
//
|
||
// Check its NUL terminated
|
||
//
|
||
|
||
if (shortcut[(info->DataLength/sizeof(WCHAR))-1] != UNICODE_NULL) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
|
||
status = ArbpGetRegistryValue(tempHandle,
|
||
shortcut,
|
||
&tempInfo
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
ExFreePool(info);
|
||
info = tempInfo;
|
||
|
||
}
|
||
|
||
ZwClose(tempHandle);
|
||
|
||
//
|
||
// We only support one level of short cuts so this should be a
|
||
// REG_RESOURCE_REQUIREMENTS_LIST
|
||
//
|
||
|
||
if (info->Type != REG_RESOURCE_REQUIREMENTS_LIST) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Extract the resource list
|
||
//
|
||
|
||
ASSERT(((PIO_RESOURCE_REQUIREMENTS_LIST) FULL_INFO_DATA(info))
|
||
->AlternativeLists == 1);
|
||
|
||
resourceList = (PIO_RESOURCE_LIST) &((PIO_RESOURCE_REQUIREMENTS_LIST)
|
||
FULL_INFO_DATA(info))->List[0];
|
||
|
||
//
|
||
// Convert the resource list into an ordering list
|
||
//
|
||
|
||
FOR_ALL_IN_ARRAY(resourceList->Descriptors,
|
||
resourceList->Count,
|
||
current) {
|
||
|
||
//
|
||
// Perform any translation that is necessary on the resources
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Translate)) {
|
||
|
||
status = (Translate)(&translated, current);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
} else {
|
||
translated = *current;
|
||
}
|
||
|
||
if (translated.Type == Arbiter->ResourceType) {
|
||
|
||
status = Arbiter->UnpackRequirement(&translated,
|
||
&start,
|
||
&end,
|
||
&dummy, //length
|
||
&dummy //alignment
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
status = ArbAddOrdering(&Arbiter->OrderingList,
|
||
start,
|
||
end
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// We're finished with info...
|
||
//
|
||
|
||
ExFreePool(info);
|
||
info = NULL;
|
||
|
||
//
|
||
// Open ReservedResources
|
||
//
|
||
|
||
ArbpWstrToUnicodeString(&unicodeString, KEY_RESERVEDRESOURCES);
|
||
InitializeObjectAttributes(&attributes,
|
||
&unicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
arbitersHandle,
|
||
(PSECURITY_DESCRIPTOR) NULL
|
||
);
|
||
|
||
|
||
status = ZwCreateKey(&tempHandle,
|
||
KEY_READ,
|
||
&attributes,
|
||
0,
|
||
(PUNICODE_STRING) NULL,
|
||
REG_OPTION_NON_VOLATILE,
|
||
NULL
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Extract the arbiter's reserved resources
|
||
//
|
||
|
||
status = ArbpGetRegistryValue(tempHandle,
|
||
ReservedResourcesName,
|
||
&info
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Check if the value we retrieved was a string and if so then it was a
|
||
// short cut to a value of that name - open it.
|
||
//
|
||
|
||
if (info->Type == REG_SZ) {
|
||
|
||
PKEY_VALUE_FULL_INFORMATION tempInfo;
|
||
PWSTR shortcut = (PWSTR) FULL_INFO_DATA(info);
|
||
|
||
//
|
||
// Check its NUL terminated
|
||
//
|
||
|
||
if (shortcut[(info->DataLength/sizeof(WCHAR))-1] != UNICODE_NULL) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
|
||
status = ArbpGetRegistryValue(tempHandle,
|
||
shortcut,
|
||
&tempInfo
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
ExFreePool(info);
|
||
info = tempInfo;
|
||
|
||
}
|
||
|
||
ZwClose(tempHandle);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
ASSERT(((PIO_RESOURCE_REQUIREMENTS_LIST) FULL_INFO_DATA(info))
|
||
->AlternativeLists == 1);
|
||
|
||
resourceList = (PIO_RESOURCE_LIST) &((PIO_RESOURCE_REQUIREMENTS_LIST)
|
||
FULL_INFO_DATA(info))->List[0];
|
||
|
||
//
|
||
// Apply the reserved ranges to the ordering
|
||
//
|
||
|
||
FOR_ALL_IN_ARRAY(resourceList->Descriptors,
|
||
resourceList->Count,
|
||
current) {
|
||
|
||
//
|
||
// Perform any translation that is necessary on the resources
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Translate)) {
|
||
|
||
status = (Translate)(&translated, current);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
} else {
|
||
translated = *current;
|
||
}
|
||
|
||
if (translated.Type == Arbiter->ResourceType) {
|
||
|
||
status = Arbiter->UnpackRequirement(&translated,
|
||
&start,
|
||
&end,
|
||
&dummy, //length
|
||
&dummy //alignment
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Add the reserved range to the reserved ordering
|
||
//
|
||
|
||
status = ArbAddOrdering(&Arbiter->ReservedList, start, end);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Prune the reserved range from the current ordering
|
||
//
|
||
|
||
status = ArbPruneOrdering(&Arbiter->OrderingList, start, end);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
ExFreePool(info);
|
||
}
|
||
|
||
//
|
||
// All done!
|
||
//
|
||
|
||
ZwClose(arbitersHandle);
|
||
|
||
#if ARB_DBG
|
||
|
||
{
|
||
PARBITER_ORDERING current;
|
||
|
||
FOR_ALL_IN_ARRAY(Arbiter->OrderingList.Orderings,
|
||
Arbiter->OrderingList.Count,
|
||
current) {
|
||
ARB_PRINT(2,
|
||
("Ordering: 0x%I64x-0x%I64x\n",
|
||
current->Start,
|
||
current->End
|
||
));
|
||
}
|
||
|
||
ARB_PRINT(2, ("\n"));
|
||
|
||
FOR_ALL_IN_ARRAY(Arbiter->ReservedList.Orderings,
|
||
Arbiter->ReservedList.Count,
|
||
current) {
|
||
ARB_PRINT(2,
|
||
("Reserved: 0x%I64x-0x%I64x\n",
|
||
current->Start,
|
||
current->End
|
||
));
|
||
}
|
||
|
||
}
|
||
|
||
#endif
|
||
|
||
ArbReleaseArbiterLock(Arbiter);
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
if (arbitersHandle) {
|
||
ZwClose(arbitersHandle);
|
||
}
|
||
|
||
if (tempHandle) {
|
||
ZwClose(tempHandle);
|
||
}
|
||
|
||
if (info) {
|
||
ExFreePool(info);
|
||
}
|
||
|
||
if (Arbiter->OrderingList.Orderings) {
|
||
ExFreePool(Arbiter->OrderingList.Orderings);
|
||
Arbiter->OrderingList.Count = 0;
|
||
Arbiter->OrderingList.Maximum = 0;
|
||
}
|
||
|
||
if (Arbiter->ReservedList.Orderings) {
|
||
ExFreePool(Arbiter->ReservedList.Orderings);
|
||
Arbiter->ReservedList.Count = 0;
|
||
Arbiter->ReservedList.Maximum = 0;
|
||
}
|
||
|
||
ArbReleaseArbiterLock(Arbiter);
|
||
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN
|
||
ArbFindSuitableRange(
|
||
PARBITER_INSTANCE Arbiter,
|
||
PARBITER_ALLOCATION_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called from AllocateEntry once we have decided where we want
|
||
to allocate from. It tries to find a free range that matches the
|
||
requirements in State while restricting its possible solutions to the range
|
||
State->CurrentMinimum to State->CurrentMaximum. On success State->Start and
|
||
State->End represent this range.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
TRUE if we found a range, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
ULONG findRangeFlags = 0;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(State->CurrentAlternative);
|
||
|
||
//
|
||
// Catch the case where we backtrack and advance past the maximum
|
||
//
|
||
|
||
if (State->CurrentMinimum > State->CurrentMaximum) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If we are asking for zero ports then trivially succeed with the minimum
|
||
// value and remember that backtracking this is a recipe for infinite loops
|
||
//
|
||
|
||
if (State->CurrentAlternative->Length == 0) {
|
||
State->End = State->Start = State->CurrentMinimum;
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// For legacy requests from IoAssignResources (directly or by way of
|
||
// HalAssignSlotResources) or IoReportResourceUsage we consider preallocated
|
||
// resources to be available for backward compatibility reasons.
|
||
//
|
||
// If we are allocating a devices boot config then we consider all other
|
||
// boot configs to be available.
|
||
//
|
||
|
||
if (State->Entry->RequestSource == ArbiterRequestLegacyReported
|
||
|| State->Entry->RequestSource == ArbiterRequestLegacyAssigned) {
|
||
|
||
State->RangeAvailableAttributes |= ARBITER_RANGE_BOOT_ALLOCATED;
|
||
}
|
||
|
||
//
|
||
// Check if null conflicts are OK...
|
||
//
|
||
|
||
if (State->Flags & ARBITER_STATE_FLAG_NULL_CONFLICT_OK) {
|
||
findRangeFlags |= RTL_RANGE_LIST_NULL_CONFLICT_OK;
|
||
}
|
||
|
||
//
|
||
// ...or we are shareable...
|
||
//
|
||
|
||
if (State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED) {
|
||
findRangeFlags |= RTL_RANGE_LIST_SHARED_OK;
|
||
}
|
||
|
||
//
|
||
// Select the first free alternative from the current alternative
|
||
//
|
||
|
||
status = RtlFindRange(
|
||
Arbiter->PossibleAllocation,
|
||
State->CurrentMinimum,
|
||
State->CurrentMaximum,
|
||
State->CurrentAlternative->Length,
|
||
State->CurrentAlternative->Alignment,
|
||
findRangeFlags,
|
||
State->RangeAvailableAttributes,
|
||
Arbiter->ConflictCallbackContext,
|
||
Arbiter->ConflictCallback,
|
||
&State->Start
|
||
);
|
||
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// We found a suitable range
|
||
//
|
||
State->End = State->Start + State->CurrentAlternative->Length - 1;
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
if (ArbShareDriverExclusive(Arbiter, State) == FALSE) {
|
||
|
||
//
|
||
// We couldn't find any range so check if we will allow this conflict
|
||
// - if so don'd fail!
|
||
//
|
||
|
||
return Arbiter->OverrideConflict(Arbiter, State);
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
ArbAddAllocation(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called from AllocateEntry once we have found a possible
|
||
solution (State->Start - State->End). It adds the ranges that will not be
|
||
available if we commit to this solution to Arbiter->PossibleAllocation.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
status = RtlAddRange(
|
||
Arbiter->PossibleAllocation,
|
||
State->Start,
|
||
State->End,
|
||
State->RangeAttributes,
|
||
RTL_RANGE_LIST_ADD_IF_CONFLICT +
|
||
(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED
|
||
? RTL_RANGE_LIST_ADD_SHARED : 0),
|
||
NULL,
|
||
State->Entry->PhysicalDeviceObject
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
ArbBacktrackAllocation(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called from AllocateEntry if the possible solution
|
||
(State->Start - State->End) does not allow us to allocate resources to
|
||
the rest of the devices being considered. It deletes the ranges that were
|
||
added to Arbiter->PossibleAllocation by AddAllocation.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We couldn't allocate for the rest of the ranges then
|
||
// backtrack
|
||
//
|
||
|
||
status = RtlDeleteRange(
|
||
Arbiter->PossibleAllocation,
|
||
State->Start,
|
||
State->End,
|
||
State->Entry->PhysicalDeviceObject
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
ARB_PRINT(2,
|
||
("\t\tBacktracking on 0x%I64x-0x%I64x for %p\n",
|
||
State->Start,
|
||
State->End,
|
||
State->Entry->PhysicalDeviceObject
|
||
));
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbPreprocessEntry(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called from AllocateEntry to allow preprocessing of
|
||
entries
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbAllocateEntry(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the core arbitration routine and is called from TestAllocation
|
||
to allocate resources for all of the entries in the allocation stack.
|
||
It calls off to various helper routines (described above) to perform this
|
||
task.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PARBITER_ALLOCATION_STATE currentState = State;
|
||
BOOLEAN backtracking = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Have we reached the end of the list? If so then we have a working
|
||
// allocation.
|
||
//
|
||
|
||
tryAllocation:
|
||
|
||
while(currentState >= State && currentState->Entry != NULL) {
|
||
|
||
//
|
||
// Do any preprocessing that is required
|
||
//
|
||
|
||
status = Arbiter->PreprocessEntry(Arbiter,currentState);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If we need to backtrack do so!
|
||
//
|
||
|
||
if (backtracking) {
|
||
|
||
ULONGLONG possibleCurrentMinimum;
|
||
|
||
backtracking = FALSE;
|
||
|
||
//
|
||
// Clear the CurrentAlternative of the *next* alternative - this will
|
||
// cause the priorities to be recalculated next time through so we
|
||
// will attempt to explore the search space again
|
||
//
|
||
// The currentState+1 is guaranteed to be safe because the only way
|
||
// we can get here is from where we currentState-- below.
|
||
//
|
||
|
||
(currentState + 1)->CurrentAlternative = NULL;
|
||
|
||
//
|
||
// We can't backtrack length 0 requests because there is nothing to
|
||
// backtrack so we would get stuck in an inifinite loop...
|
||
//
|
||
|
||
if (currentState->CurrentAlternative->Length == 0) {
|
||
goto failAllocation;
|
||
}
|
||
|
||
//
|
||
// Backtrack
|
||
//
|
||
|
||
Arbiter->BacktrackAllocation(Arbiter, currentState);
|
||
|
||
//
|
||
// Reduce allocation window to not include the range we backtracked
|
||
// and check that that doesn't underflow the minimum or wrap
|
||
//
|
||
|
||
possibleCurrentMinimum = currentState->Start - 1;
|
||
|
||
if (possibleCurrentMinimum > currentState->CurrentMinimum // wrapped
|
||
|| possibleCurrentMinimum < currentState->CurrentAlternative->Minimum) {
|
||
|
||
//
|
||
// We have run out space in this alternative move on to the next
|
||
//
|
||
|
||
goto continueWithNextAllocationRange;
|
||
|
||
} else {
|
||
|
||
currentState->CurrentMaximum = possibleCurrentMinimum;
|
||
|
||
//
|
||
// Get back into arbitrating at the right point
|
||
//
|
||
|
||
goto continueWithNextSuitableRange;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Try to allocate for this entry
|
||
//
|
||
|
||
continueWithNextAllocationRange:
|
||
|
||
while (Arbiter->GetNextAllocationRange(Arbiter, currentState)) {
|
||
|
||
ARB_INDENT(2, (ULONG)(currentState - State));
|
||
|
||
ARB_PRINT(2,
|
||
("Testing 0x%I64x-0x%I64x %s\n",
|
||
currentState->CurrentMinimum,
|
||
currentState->CurrentMaximum,
|
||
currentState->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED ?
|
||
"shared" : "non-shared"
|
||
));
|
||
|
||
continueWithNextSuitableRange:
|
||
|
||
while (Arbiter->FindSuitableRange(Arbiter, currentState)) {
|
||
|
||
//
|
||
// We found a possible solution
|
||
//
|
||
|
||
ARB_INDENT(2, (ULONG)(currentState - State));
|
||
|
||
if (currentState->CurrentAlternative->Length != 0) {
|
||
|
||
ARB_PRINT(2,
|
||
("Possible solution for %p = 0x%I64x-0x%I64x, %s\n",
|
||
currentState->Entry->PhysicalDeviceObject,
|
||
currentState->Start,
|
||
currentState->End,
|
||
currentState->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED ?
|
||
"shared" : "non-shared"
|
||
));
|
||
|
||
//
|
||
// Update the arbiter with the possible allocation
|
||
//
|
||
|
||
Arbiter->AddAllocation(Arbiter, currentState);
|
||
|
||
} else {
|
||
|
||
ARB_PRINT(2,
|
||
("Zero length solution solution for %p = 0x%I64x-0x%I64x, %s\n",
|
||
currentState->Entry->PhysicalDeviceObject,
|
||
currentState->Start,
|
||
currentState->End,
|
||
currentState->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED ?
|
||
"shared" : "non-shared"
|
||
));
|
||
|
||
//
|
||
// Set the result in the arbiter appropriatley so that we
|
||
// don't try and translate this zero requirement - it won't!
|
||
//
|
||
|
||
currentState->Entry->Result = ArbiterResultNullRequest;
|
||
}
|
||
|
||
//
|
||
// Move on to the next entry
|
||
//
|
||
|
||
currentState++;
|
||
goto tryAllocation;
|
||
}
|
||
}
|
||
|
||
failAllocation:
|
||
|
||
//
|
||
// We couldn't allocate for this device
|
||
//
|
||
|
||
if (currentState == State) {
|
||
|
||
//
|
||
// We are at the top of the allocation stack to we can't backtrack -
|
||
// *** GAME OVER ***
|
||
//
|
||
|
||
return STATUS_UNSUCCESSFUL;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Backtrack and try again
|
||
//
|
||
|
||
ARB_INDENT(2, (ULONG)(currentState - State));
|
||
|
||
ARB_PRINT(2,
|
||
("Allocation failed for %p - backtracking\n",
|
||
currentState->Entry->PhysicalDeviceObject
|
||
));
|
||
|
||
backtracking = TRUE;
|
||
|
||
//
|
||
// Pop the last state off the stack and try a different path
|
||
//
|
||
|
||
currentState--;
|
||
goto tryAllocation;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have successfully allocated for all ranges so fill in the allocation
|
||
//
|
||
|
||
currentState = State;
|
||
|
||
while (currentState->Entry != NULL) {
|
||
|
||
status = Arbiter->PackResource(
|
||
currentState->CurrentAlternative->Descriptor,
|
||
currentState->Start,
|
||
currentState->Entry->Assignment
|
||
);
|
||
|
||
ASSERT(NT_SUCCESS(status));
|
||
|
||
//
|
||
// Remember the alternative we chose from so we can retrieve it during retest
|
||
//
|
||
|
||
currentState->Entry->SelectedAlternative
|
||
= currentState->CurrentAlternative->Descriptor;
|
||
|
||
ARB_PRINT(2,
|
||
("Assigned - 0x%I64x-0x%I64x\n",
|
||
currentState->Start,
|
||
currentState->End
|
||
));
|
||
|
||
currentState++;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
ArbGetNextAllocationRange(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN OUT PARBITER_ALLOCATION_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts to find the next range where allocation should be
|
||
tried. It updates State->CurrentMinimum, State->CurrentMaximum and
|
||
State->CurrentAlternative to indicate this range.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter
|
||
|
||
State - The state of the current arbitration
|
||
|
||
Return Value:
|
||
|
||
TRUE if a range to attemp allocation in is found, FALSE otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PARBITER_ALTERNATIVE current, lowestAlternative;
|
||
ULONGLONG min, max;
|
||
PARBITER_ORDERING ordering;
|
||
|
||
|
||
for (;;) {
|
||
|
||
if (State->CurrentAlternative) {
|
||
|
||
//
|
||
// Update the priority of the alternative we selected last time
|
||
//
|
||
|
||
ArbpUpdatePriority(Arbiter, State->CurrentAlternative);
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is the first time we are looking at this alternative or a
|
||
// backtrack - either way we need to update all the priorities
|
||
//
|
||
|
||
FOR_ALL_IN_ARRAY(State->Alternatives,
|
||
State->AlternativeCount,
|
||
current) {
|
||
|
||
current->Priority = ARBITER_PRIORITY_NULL;
|
||
ArbpUpdatePriority(Arbiter, current);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Find the lowest priority of the alternatives
|
||
//
|
||
|
||
lowestAlternative = State->Alternatives;
|
||
|
||
FOR_ALL_IN_ARRAY(State->Alternatives + 1,
|
||
State->AlternativeCount - 1,
|
||
current) {
|
||
|
||
if (current->Priority < lowestAlternative->Priority) {
|
||
lowestAlternative = current;
|
||
}
|
||
}
|
||
|
||
ARB_INDENT(2, (ULONG)(State - Arbiter->AllocationStack));
|
||
|
||
//
|
||
// Check if we have run out of allocation ranges
|
||
//
|
||
|
||
if (lowestAlternative->Priority == ARBITER_PRIORITY_EXHAUSTED) {
|
||
|
||
if (lowestAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED) {
|
||
|
||
ARB_PRINT(2,("Fixed alternative exhausted\n"));
|
||
|
||
} else {
|
||
|
||
ARB_PRINT(2,("Alternative exhausted\n"));
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
ARB_PRINT(2,(
|
||
"LowestAlternative: [%i] 0x%I64x-0x%I64x L=0x%08x A=0x%08x\n",
|
||
lowestAlternative->Priority,
|
||
lowestAlternative->Minimum,
|
||
lowestAlternative->Maximum,
|
||
lowestAlternative->Length,
|
||
lowestAlternative->Alignment
|
||
));
|
||
|
||
}
|
||
|
||
//
|
||
// Check if we are now allowing reserved ranges
|
||
//
|
||
|
||
if (lowestAlternative->Priority == ARBITER_PRIORITY_RESERVED
|
||
|| lowestAlternative->Priority == ARBITER_PRIORITY_PREFERRED_RESERVED) {
|
||
|
||
//
|
||
// Set min and max to be the Minimum and Maximum that the descriptor
|
||
// specified ignoring any reservations or orderings - this is our
|
||
// last chance
|
||
//
|
||
|
||
min = lowestAlternative->Minimum;
|
||
max = lowestAlternative->Maximum;
|
||
|
||
ARB_INDENT(2, (ULONG)(State - Arbiter->AllocationStack));
|
||
|
||
ARB_PRINT(2,("Allowing reserved ranges\n"));
|
||
|
||
} else {
|
||
|
||
ASSERT(ORDERING_INDEX_FROM_PRIORITY(lowestAlternative->Priority) <
|
||
Arbiter->OrderingList.Count);
|
||
|
||
//
|
||
// Locate the ordering we match
|
||
//
|
||
|
||
ordering = &Arbiter->OrderingList.Orderings
|
||
[ORDERING_INDEX_FROM_PRIORITY(lowestAlternative->Priority)];
|
||
|
||
//
|
||
// Make sure they overlap and are big enough - this is just paranoia
|
||
//
|
||
|
||
ASSERT(INTERSECT(lowestAlternative->Minimum,
|
||
lowestAlternative->Maximum,
|
||
ordering->Start,
|
||
ordering->End)
|
||
&& INTERSECT_SIZE(lowestAlternative->Minimum,
|
||
lowestAlternative->Maximum,
|
||
ordering->Start,
|
||
ordering->End) >= lowestAlternative->Length);
|
||
|
||
//
|
||
// Calculate the allocation range
|
||
//
|
||
|
||
min = __max(lowestAlternative->Minimum, ordering->Start);
|
||
|
||
max = __min(lowestAlternative->Maximum, ordering->End);
|
||
|
||
}
|
||
|
||
//
|
||
// If this is a length 0 requirement then succeed now and avoid much
|
||
// trauma later
|
||
//
|
||
|
||
if (lowestAlternative->Length == 0) {
|
||
|
||
min = lowestAlternative->Minimum;
|
||
max = lowestAlternative->Maximum;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Trim range to match alignment.
|
||
//
|
||
|
||
min += lowestAlternative->Alignment - 1;
|
||
min -= min % lowestAlternative->Alignment;
|
||
|
||
if ((lowestAlternative->Length - 1) > (max - min)) {
|
||
|
||
ARB_INDENT(3, (ULONG)(State - Arbiter->AllocationStack));
|
||
ARB_PRINT(3, ("Range cannot be aligned ... Skipping\n"));
|
||
|
||
//
|
||
// Set CurrentAlternative so we will update the priority of this
|
||
// alternative
|
||
//
|
||
|
||
State->CurrentAlternative = lowestAlternative;
|
||
continue;
|
||
}
|
||
|
||
max -= lowestAlternative->Length - 1;
|
||
max -= max % lowestAlternative->Alignment;
|
||
max += lowestAlternative->Length - 1;
|
||
|
||
}
|
||
|
||
//
|
||
// Check if we handed back the same range last time, for the same
|
||
// alternative, if so try to find another range
|
||
//
|
||
|
||
if (min == State->CurrentMinimum
|
||
&& max == State->CurrentMaximum
|
||
&& State->CurrentAlternative == lowestAlternative) {
|
||
|
||
ARB_INDENT(2, (ULONG)(State - Arbiter->AllocationStack));
|
||
|
||
ARB_PRINT(2,
|
||
("Skipping identical allocation range\n"
|
||
));
|
||
|
||
continue;
|
||
}
|
||
|
||
State->CurrentMinimum = min;
|
||
State->CurrentMaximum = max;
|
||
State->CurrentAlternative = lowestAlternative;
|
||
|
||
ARB_INDENT(2, (ULONG)(State - Arbiter->AllocationStack));
|
||
ARB_PRINT(1, ("AllocationRange: 0x%I64x-0x%I64x\n", min, max));
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbpGetRegistryValue(
|
||
IN HANDLE KeyHandle,
|
||
IN PWSTR ValueName,
|
||
OUT PKEY_VALUE_FULL_INFORMATION *Information
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to retrieve the data for a registry key's value.
|
||
This is done by querying the value of the key with a zero-length buffer
|
||
to determine the size of the value, and then allocating a buffer and
|
||
actually querying the value into the buffer.
|
||
|
||
It is the responsibility of the caller to free the buffer.
|
||
|
||
Arguments:
|
||
|
||
KeyHandle - Supplies the key handle whose value is to be queried
|
||
|
||
ValueName - Supplies the null-terminated Unicode name of the value.
|
||
|
||
Information - Returns a pointer to the allocated data buffer.
|
||
|
||
Return Value:
|
||
|
||
The function value is the final status of the query operation.
|
||
|
||
Note:
|
||
|
||
The same as IopGetRegistryValue - it allows us to share the arbiter
|
||
code with pci.sys
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING unicodeString;
|
||
NTSTATUS status;
|
||
PKEY_VALUE_FULL_INFORMATION infoBuffer;
|
||
ULONG keyValueLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
RtlInitUnicodeString( &unicodeString, ValueName );
|
||
|
||
//
|
||
// Figure out how big the data value is so that a buffer of the
|
||
// appropriate size can be allocated.
|
||
//
|
||
|
||
status = ZwQueryValueKey( KeyHandle,
|
||
&unicodeString,
|
||
KeyValueFullInformationAlign64,
|
||
(PVOID) NULL,
|
||
0,
|
||
&keyValueLength );
|
||
if (status != STATUS_BUFFER_OVERFLOW &&
|
||
status != STATUS_BUFFER_TOO_SMALL) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer large enough to contain the entire key data value.
|
||
//
|
||
|
||
infoBuffer = ExAllocatePoolWithTag( PagedPool,
|
||
keyValueLength,
|
||
ARBITER_MISC_TAG
|
||
);
|
||
|
||
if (!infoBuffer) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Query the data for the key value.
|
||
//
|
||
|
||
status = ZwQueryValueKey( KeyHandle,
|
||
&unicodeString,
|
||
KeyValueFullInformationAlign64,
|
||
infoBuffer,
|
||
keyValueLength,
|
||
&keyValueLength );
|
||
if (!NT_SUCCESS( status )) {
|
||
ExFreePool( infoBuffer );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Everything worked, so simply return the address of the allocated
|
||
// buffer to the caller, who is now responsible for freeing it.
|
||
//
|
||
|
||
*Information = infoBuffer;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
#define ARBITER_ORDERING_LIST_INITIAL_SIZE 16
|
||
|
||
NTSTATUS
|
||
ArbInitializeOrderingList(
|
||
IN OUT PARBITER_ORDERING_LIST List
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine inititialize an arbiter ordering list.
|
||
|
||
Arguments:
|
||
|
||
List - The list to be initialized
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ASSERT(List);
|
||
|
||
List->Orderings = ExAllocatePoolWithTag(PagedPool,
|
||
ARBITER_ORDERING_LIST_INITIAL_SIZE *
|
||
sizeof(ARBITER_ORDERING),
|
||
ARBITER_ORDERING_LIST_TAG
|
||
);
|
||
|
||
if (!List->Orderings) {
|
||
List->Maximum = 0;
|
||
List->Count = 0;
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
List->Count = 0;
|
||
List->Maximum = ARBITER_ORDERING_LIST_INITIAL_SIZE;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbCopyOrderingList(
|
||
OUT PARBITER_ORDERING_LIST Destination,
|
||
IN PARBITER_ORDERING_LIST Source
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine copies an arbiter ordering list.
|
||
|
||
Arguments:
|
||
|
||
Destination - An uninitialized arbiter ordering list where the data
|
||
should be copied from
|
||
|
||
Source - Arbiter ordering list to be copied
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
|
||
PAGED_CODE()
|
||
|
||
ASSERT(Source->Count <= Source->Maximum);
|
||
ASSERT(Source->Maximum > 0);
|
||
|
||
Destination->Orderings =
|
||
ExAllocatePoolWithTag(PagedPool,
|
||
Source->Maximum * sizeof(ARBITER_ORDERING),
|
||
ARBITER_ORDERING_LIST_TAG
|
||
);
|
||
|
||
if (Destination->Orderings == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
Destination->Count = Source->Count;
|
||
Destination->Maximum = Source->Maximum;
|
||
|
||
if (Source->Count > 0) {
|
||
|
||
RtlCopyMemory(Destination->Orderings,
|
||
Source->Orderings,
|
||
Source->Count * sizeof(ARBITER_ORDERING)
|
||
);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbAddOrdering(
|
||
OUT PARBITER_ORDERING_LIST List,
|
||
IN ULONGLONG Start,
|
||
IN ULONGLONG End
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine adds the range Start-End to the end of the ordering list. No
|
||
checking for overlaps or pruning is done (see ArbpPruneOrdering)
|
||
|
||
Arguments:
|
||
|
||
OrderingList - The list where the range should be added.
|
||
|
||
Start - The start of the range to be added.
|
||
|
||
End - The end of the range to be added.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAGED_CODE()
|
||
|
||
//
|
||
// Validate parameters
|
||
//
|
||
|
||
if (End < Start) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Check if the buffer is full
|
||
//
|
||
|
||
if (List->Count == List->Maximum) {
|
||
|
||
PARBITER_ORDERING temp;
|
||
|
||
//
|
||
// Out of space - grow the buffer
|
||
//
|
||
|
||
temp = ExAllocatePoolWithTag(PagedPool,
|
||
(List->Count + ARBITER_ORDERING_GROW_SIZE) *
|
||
sizeof(ARBITER_ORDERING),
|
||
ARBITER_ORDERING_LIST_TAG
|
||
);
|
||
|
||
if (!temp) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// If we had any orderings copy them
|
||
//
|
||
|
||
if (List->Orderings) {
|
||
|
||
RtlCopyMemory(temp,
|
||
List->Orderings,
|
||
List->Count * sizeof(ARBITER_ORDERING)
|
||
);
|
||
|
||
ExFreePool(List->Orderings);
|
||
}
|
||
|
||
List->Maximum += ARBITER_ORDERING_GROW_SIZE;
|
||
List->Orderings = temp;
|
||
|
||
}
|
||
|
||
//
|
||
// Add the entry to the list
|
||
//
|
||
|
||
List->Orderings[List->Count].Start = Start;
|
||
List->Orderings[List->Count].End = End;
|
||
List->Count++;
|
||
|
||
ASSERT(List->Count <= List->Maximum);
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbPruneOrdering(
|
||
IN OUT PARBITER_ORDERING_LIST OrderingList,
|
||
IN ULONGLONG Start,
|
||
IN ULONGLONG End
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine removes the range Start-End from all entries in the ordering
|
||
list, splitting ranges into two or deleting them as necessary.
|
||
|
||
Arguments:
|
||
|
||
OrderingList - The list to be pruned.
|
||
|
||
Start - The start of the range to be deleted.
|
||
|
||
End - The end of the range to be deleted.
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
Note:
|
||
|
||
In the comments below *** represents the range Start - End and --- the range
|
||
current->Start - current->End.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS status;
|
||
PARBITER_ORDERING current, currentInsert, newOrdering = NULL, temp = NULL;
|
||
USHORT count;
|
||
|
||
PAGED_CODE()
|
||
|
||
ASSERT(OrderingList);
|
||
ASSERT(OrderingList->Orderings);
|
||
|
||
//
|
||
// Validate parameters
|
||
//
|
||
|
||
if (End < Start) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Allocate a buffer big enough for all eventualities
|
||
//
|
||
|
||
newOrdering = ExAllocatePoolWithTag(PagedPool,
|
||
(OrderingList->Count * 2 + 1) *
|
||
sizeof(ARBITER_ORDERING),
|
||
ARBITER_ORDERING_LIST_TAG
|
||
);
|
||
|
||
if (!newOrdering) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
currentInsert = newOrdering;
|
||
|
||
//
|
||
// Do we have a current ordering?
|
||
//
|
||
|
||
if (OrderingList->Count > 0) {
|
||
|
||
//
|
||
// Iterate through the current ordering and prune accordingly
|
||
//
|
||
|
||
FOR_ALL_IN_ARRAY(OrderingList->Orderings, OrderingList->Count, current) {
|
||
|
||
if (End < current->Start || Start > current->End) {
|
||
|
||
//
|
||
// **** or ****
|
||
// ---- ----
|
||
//
|
||
// We don't overlap so copy the range unchanged
|
||
//
|
||
|
||
*currentInsert++ = *current;
|
||
|
||
} else if (Start > current->Start) {
|
||
|
||
if (End < current->End) {
|
||
|
||
//
|
||
// ****
|
||
// --------
|
||
//
|
||
// Split the range into two
|
||
//
|
||
|
||
currentInsert->Start = End + 1;
|
||
currentInsert->End = current->End;
|
||
currentInsert++;
|
||
|
||
currentInsert->Start = current->Start;
|
||
currentInsert->End = Start - 1;
|
||
currentInsert++;
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// **** or ****
|
||
// -------- --------
|
||
//
|
||
// Prune the end of the range
|
||
//
|
||
|
||
ASSERT(End >= current->End);
|
||
|
||
currentInsert->Start = current->Start;
|
||
currentInsert->End = Start - 1;
|
||
currentInsert++;
|
||
}
|
||
} else {
|
||
|
||
ASSERT(Start <= current->Start);
|
||
|
||
if (End < current->End) {
|
||
|
||
//
|
||
// **** or ****
|
||
// -------- --------
|
||
//
|
||
// Prune the start of the range
|
||
//
|
||
|
||
currentInsert->Start = End + 1;
|
||
currentInsert->End = current->End;
|
||
currentInsert++;
|
||
|
||
} else {
|
||
|
||
ASSERT(End >= current->End);
|
||
|
||
//
|
||
// ******** or ********
|
||
// ---- --------
|
||
//
|
||
// Don't copy the range (ie. Delete it)
|
||
//
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
ASSERT(currentInsert - newOrdering >= 0);
|
||
|
||
count = (USHORT)(currentInsert - newOrdering);
|
||
|
||
//
|
||
// Check if we have any orderings left
|
||
//
|
||
|
||
if (count > 0) {
|
||
|
||
if (count > OrderingList->Maximum) {
|
||
|
||
//
|
||
// There isn't enough space so allocate a new buffer
|
||
//
|
||
|
||
temp =
|
||
ExAllocatePoolWithTag(PagedPool,
|
||
count * sizeof(ARBITER_ORDERING),
|
||
ARBITER_ORDERING_LIST_TAG
|
||
);
|
||
|
||
if (!temp) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
if (OrderingList->Orderings) {
|
||
ExFreePool(OrderingList->Orderings);
|
||
}
|
||
|
||
OrderingList->Orderings = temp;
|
||
OrderingList->Maximum = count;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Copy the new ordering
|
||
//
|
||
|
||
RtlCopyMemory(OrderingList->Orderings,
|
||
newOrdering,
|
||
count * sizeof(ARBITER_ORDERING)
|
||
);
|
||
}
|
||
|
||
//
|
||
// Free our temporary buffer
|
||
//
|
||
|
||
ExFreePool(newOrdering);
|
||
|
||
OrderingList->Count = count;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
if (newOrdering) {
|
||
ExFreePool(newOrdering);
|
||
}
|
||
|
||
if (temp) {
|
||
ExFreePool(temp);
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
VOID
|
||
ArbFreeOrderingList(
|
||
IN PARBITER_ORDERING_LIST List
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Frees storage associated with an ordering list.
|
||
Reverses ArbInitializeOrderingList.
|
||
|
||
Arguments:
|
||
|
||
List - The list to be fred
|
||
|
||
Return Value:
|
||
|
||
None
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (List->Orderings) {
|
||
ASSERT(List->Maximum);
|
||
ExFreePool(List->Orderings);
|
||
}
|
||
|
||
List->Count = 0;
|
||
List->Maximum = 0;
|
||
List->Orderings = NULL;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
ArbOverrideConflict(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the default implementation of override conflict which
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the conflict is allowable, false otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PRTL_RANGE current;
|
||
RTL_RANGE_LIST_ITERATOR iterator;
|
||
BOOLEAN ok = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (!(State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED)) {
|
||
return FALSE;
|
||
}
|
||
|
||
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
|
||
|
||
//
|
||
// Only test the overlapping ones
|
||
//
|
||
|
||
if (INTERSECT(current->Start, current->End, State->CurrentMinimum, State->CurrentMaximum)) {
|
||
|
||
|
||
//
|
||
// Check if we should ignore the range because of its attributes
|
||
//
|
||
|
||
if (current->Attributes & State->RangeAvailableAttributes) {
|
||
|
||
//
|
||
// We DON'T set ok to true because we are just ignoring the range,
|
||
// as RtlFindRange would have and thus it can't be the cause of
|
||
// RtlFindRange failing, so ignoring it can't fix the conflict.
|
||
//
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Check if we are conflicting with ourselves AND the conflicting range
|
||
// is a fixed requirement
|
||
//
|
||
|
||
if (current->Owner == State->Entry->PhysicalDeviceObject
|
||
&& State->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED) {
|
||
|
||
State->Start=State->CurrentMinimum;
|
||
State->End=State->CurrentMaximum;
|
||
|
||
ok = TRUE;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// The conflict is still valid
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
}
|
||
return ok;
|
||
}
|
||
|
||
VOID
|
||
ArbpUpdatePriority(
|
||
PARBITER_INSTANCE Arbiter,
|
||
PARBITER_ALTERNATIVE Alternative
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine updates the priority of an arbiter alternative.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The arbiter we are operating on
|
||
|
||
Alternative - The alternative currently being considered
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
Note:
|
||
|
||
The priorities are a LONG values organised as:
|
||
|
||
<------Preferred priorities-----> <-----Ordinary Priorities----->
|
||
|
||
MINLONG--------------------------0-----------------------------MAXLONG
|
||
^ ^ ^ ^
|
||
| | | |
|
||
NULL PREFERRED_RESERVED | |
|
||
RESERVED |
|
||
EXHAUSTED
|
||
|
||
An ordinary priority is calculated the (index + 1) of the next ordering it
|
||
intersects with (and has enough space for an allocation).
|
||
|
||
A preferred priority is the ordinary priority * - 1
|
||
|
||
In this way by examining each of the alternatives in priority order (lowest
|
||
first) we achieve the desired allocation order of:
|
||
|
||
(1) Preferred alternative with non-reserved resources
|
||
(2) Alternatives with non-reserved resources
|
||
(3) Preferred reserved resources
|
||
(4) Reserved Resources
|
||
|
||
MAXLONG the worst priority indicates that there are no more allocation ranges
|
||
left.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PARBITER_ORDERING ordering;
|
||
BOOLEAN preferred;
|
||
LONG priority;
|
||
|
||
PAGED_CODE();
|
||
|
||
priority = Alternative->Priority;
|
||
|
||
//
|
||
// If we have already tried the reserved resources then we are out of luck!
|
||
//
|
||
|
||
if (priority == ARBITER_PRIORITY_RESERVED
|
||
|| priority == ARBITER_PRIORITY_PREFERRED_RESERVED) {
|
||
|
||
Alternative->Priority = ARBITER_PRIORITY_EXHAUSTED;
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Check if this is a preferred value - we treat them specially
|
||
//
|
||
|
||
preferred = Alternative->Descriptor->Option & IO_RESOURCE_PREFERRED;
|
||
|
||
//
|
||
// If priority is NULL then we haven't started calculating one so we
|
||
// should start the search from the initial ordering
|
||
//
|
||
|
||
if (priority == ARBITER_PRIORITY_NULL) {
|
||
|
||
ordering = Arbiter->OrderingList.Orderings;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If we are a fixed resource then there is no point
|
||
// in trying to find another range - it will be the
|
||
// same and thus still conflict. Mark this alternative as
|
||
// exhausted
|
||
//
|
||
|
||
if (Alternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED) {
|
||
|
||
Alternative->Priority = ARBITER_PRIORITY_EXHAUSTED;
|
||
|
||
return;
|
||
}
|
||
|
||
ASSERT(ORDERING_INDEX_FROM_PRIORITY(Alternative->Priority) <
|
||
Arbiter->OrderingList.Count);
|
||
|
||
ordering = &Arbiter->OrderingList.Orderings
|
||
[ORDERING_INDEX_FROM_PRIORITY(Alternative->Priority) + 1];
|
||
|
||
}
|
||
|
||
//
|
||
// Now find the first member of the assignent ordering for this arbiter
|
||
// where we have an overlap big enough
|
||
//
|
||
|
||
FOR_REST_IN_ARRAY(Arbiter->OrderingList.Orderings,
|
||
Arbiter->OrderingList.Count,
|
||
ordering) {
|
||
|
||
//
|
||
// Is the ordering applicable?
|
||
//
|
||
|
||
if (INTERSECT(Alternative->Minimum, Alternative->Maximum,
|
||
ordering->Start, ordering->End)
|
||
&& INTERSECT_SIZE(Alternative->Minimum, Alternative->Maximum,
|
||
ordering->Start,ordering->End) >= Alternative->Length) {
|
||
|
||
//
|
||
// This is out guy, calculate his priority
|
||
//
|
||
|
||
Alternative->Priority = (LONG)(ordering - Arbiter->OrderingList.Orderings + 1);
|
||
|
||
//
|
||
// Preferred priorities are -ve
|
||
//
|
||
|
||
if (preferred) {
|
||
Alternative->Priority *= -1;
|
||
}
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// We have runout of non-reserved resources so try the reserved ones
|
||
//
|
||
|
||
if (preferred) {
|
||
Alternative->Priority = ARBITER_PRIORITY_PREFERRED_RESERVED;
|
||
} else {
|
||
Alternative->Priority = ARBITER_PRIORITY_RESERVED;
|
||
}
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
ArbAddReserved(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PIO_RESOURCE_DESCRIPTOR Requirement OPTIONAL,
|
||
IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Resource OPTIONAL
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
BOOLEAN
|
||
ArbpQueryConflictCallback(
|
||
IN PVOID Context,
|
||
IN PRTL_RANGE Range
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This call back is called from FindSuitableRange (via RtlFindRange) when we
|
||
encounter an conflicting range.
|
||
|
||
Arguments:
|
||
|
||
Context - Actually a PRTL_RANGE * where we store the range we conflicted
|
||
with.
|
||
|
||
Range - The range we conflict with.
|
||
|
||
Return Value:
|
||
|
||
FALSE
|
||
|
||
--*/
|
||
|
||
{
|
||
PRTL_RANGE *conflictingRange = (PRTL_RANGE*)Context;
|
||
|
||
PAGED_CODE();
|
||
|
||
ARB_PRINT(2,("Possible conflict: (%p) 0x%I64x-0x%I64x Owner: %p",
|
||
Range,
|
||
Range->Start,
|
||
Range->End,
|
||
Range->Owner
|
||
));
|
||
|
||
//
|
||
// Remember the conflicting range
|
||
//
|
||
|
||
*conflictingRange = Range;
|
||
|
||
//
|
||
// We want to allow the rest of FindSuitableRange to determine if this really
|
||
// is a conflict.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbQueryConflict(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PDEVICE_OBJECT PhysicalDeviceObject,
|
||
IN PIO_RESOURCE_DESCRIPTOR ConflictingResource,
|
||
OUT PULONG ConflictCount,
|
||
OUT PARBITER_CONFLICT_INFO *Conflicts
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine examines the arbiter state and returns a list of devices that
|
||
conflict with ConflictingResource
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The arbiter to examine conflicts in
|
||
|
||
ConflictingResource - The resource we want to know the conflicts with
|
||
|
||
ConflictCount - On success contains the number of conflicts detected
|
||
|
||
ConflictList - On success contains a pointer to an array of conflicting
|
||
devices
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
{
|
||
//
|
||
// NTRAID #98568 - 2000/03/31 - andrewth
|
||
// ArbQueryConflict needs to be redesigned
|
||
//
|
||
|
||
NTSTATUS status;
|
||
RTL_RANGE_LIST backupAllocation;
|
||
BOOLEAN backedUp = FALSE;
|
||
ARBITER_LIST_ENTRY entry;
|
||
ARBITER_ALLOCATION_STATE state;
|
||
ARBITER_ALTERNATIVE alternative;
|
||
ULONG count = 0, size = 10;
|
||
PRTL_RANGE conflictingRange;
|
||
PARBITER_CONFLICT_INFO conflictInfo = NULL;
|
||
PVOID savedContext;
|
||
PRTL_CONFLICT_RANGE_CALLBACK savedCallback;
|
||
ULONG sz;
|
||
|
||
PAGED_CODE();
|
||
|
||
ASSERT(PhysicalDeviceObject);
|
||
ASSERT(ConflictingResource);
|
||
ASSERT(ConflictCount);
|
||
ASSERT(Conflicts);
|
||
//
|
||
// Set up our conflict callback
|
||
//
|
||
savedCallback = Arbiter->ConflictCallback;
|
||
savedContext = Arbiter->ConflictCallbackContext;
|
||
Arbiter->ConflictCallback = ArbpQueryConflictCallback;
|
||
Arbiter->ConflictCallbackContext = &conflictingRange;
|
||
|
||
//
|
||
// If there is a transaction in progress then we need to backup the
|
||
// the possible allocation so we can restore it when we are done.
|
||
//
|
||
|
||
if (Arbiter->TransactionInProgress) {
|
||
|
||
RtlInitializeRangeList(&backupAllocation);
|
||
|
||
status = RtlCopyRangeList(&backupAllocation, Arbiter->PossibleAllocation);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
|
||
backedUp = TRUE;
|
||
}
|
||
|
||
//
|
||
// Fake up the allocation state
|
||
//
|
||
|
||
|
||
status = RtlCopyRangeList(Arbiter->PossibleAllocation, Arbiter->Allocation);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
status = ArbpBuildAlternative(Arbiter, ConflictingResource, &alternative);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
RtlZeroMemory(&state, sizeof(ARBITER_ALLOCATION_STATE));
|
||
|
||
state.Start = alternative.Minimum;
|
||
state.End = alternative.Maximum;
|
||
state.CurrentMinimum = state.Start;
|
||
state.CurrentMaximum = state.End;
|
||
state.CurrentAlternative = &alternative;
|
||
state.AlternativeCount = 1;
|
||
state.Alternatives = &alternative;
|
||
state.Flags = ARBITER_STATE_FLAG_CONFLICT;
|
||
state.Entry = &entry;
|
||
|
||
RtlZeroMemory(&entry, sizeof(ARBITER_LIST_ENTRY));
|
||
entry.RequestSource = ArbiterRequestPnpEnumerated;
|
||
entry.PhysicalDeviceObject = PhysicalDeviceObject;
|
||
|
||
if (!NT_SUCCESS(IoGetDeviceProperty(PhysicalDeviceObject,DevicePropertyLegacyBusType,sizeof(entry.InterfaceType),&entry.InterfaceType,&sz))) {
|
||
entry.InterfaceType = Isa; // not what I want to do! However this has the right effect - good enough for conflict detection
|
||
}
|
||
if (!NT_SUCCESS(IoGetDeviceProperty(PhysicalDeviceObject,DevicePropertyBusNumber,sizeof(entry.InterfaceType),&entry.BusNumber,&sz))) {
|
||
entry.BusNumber = 0; // not what I want to do! However this has the right effect - good enough for conflict detection
|
||
}
|
||
|
||
//
|
||
// Initialize the return buffers
|
||
//
|
||
|
||
conflictInfo = ExAllocatePoolWithTag(PagedPool,
|
||
size * sizeof(ARBITER_CONFLICT_INFO),
|
||
ARBITER_CONFLICT_INFO_TAG
|
||
);
|
||
|
||
if (!conflictInfo) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Perform any necessary preprocessing
|
||
//
|
||
|
||
status = Arbiter->PreprocessEntry(Arbiter, &state);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
//
|
||
// Remove self from list of possible allocations
|
||
// status may be set, but can be ignored
|
||
// we take ourself out of test completely, so that a user can
|
||
// pick new values in context of rest of the world
|
||
// if we decide to use RtlDeleteRange instead
|
||
// make sure we do it for every alias formed in PreprocessEntry
|
||
//
|
||
|
||
status = RtlDeleteOwnersRanges(Arbiter->PossibleAllocation,
|
||
state.Entry->PhysicalDeviceObject
|
||
);
|
||
|
||
//
|
||
// Keep trying to find a suitable range and each time we fail remember why.
|
||
//
|
||
conflictingRange = NULL;
|
||
state.CurrentMinimum = state.Start;
|
||
state.CurrentMaximum = state.End;
|
||
|
||
while (!Arbiter->FindSuitableRange(Arbiter, &state)) {
|
||
|
||
if (count == size) {
|
||
|
||
//
|
||
// We need to resize the return buffer
|
||
//
|
||
|
||
PARBITER_CONFLICT_INFO temp = conflictInfo;
|
||
|
||
size += 5;
|
||
|
||
conflictInfo =
|
||
ExAllocatePoolWithTag(PagedPool,
|
||
size * sizeof(ARBITER_CONFLICT_INFO),
|
||
ARBITER_CONFLICT_INFO_TAG
|
||
);
|
||
|
||
if (!conflictInfo) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
conflictInfo = temp;
|
||
goto cleanup;
|
||
}
|
||
|
||
RtlCopyMemory(conflictInfo,
|
||
temp,
|
||
count * sizeof(ARBITER_CONFLICT_INFO)
|
||
);
|
||
|
||
ExFreePool(temp);
|
||
|
||
}
|
||
|
||
if (conflictingRange != NULL) {
|
||
conflictInfo[count].OwningObject = conflictingRange->Owner;
|
||
conflictInfo[count].Start = conflictingRange->Start;
|
||
conflictInfo[count].End = conflictingRange->End;
|
||
count++;
|
||
|
||
//
|
||
// Delete the range we conflicted with so we don't loop forever
|
||
//
|
||
#if 0
|
||
status = RtlDeleteRange(Arbiter->PossibleAllocation,
|
||
conflictingRange->Start,
|
||
conflictingRange->End,
|
||
conflictingRange->Owner
|
||
);
|
||
#endif
|
||
status = RtlDeleteOwnersRanges(Arbiter->PossibleAllocation,
|
||
conflictingRange->Owner
|
||
);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// someone isn't playing by the rules (such as ACPI!)
|
||
//
|
||
ARB_PRINT(0,("Conflict detected - but someone hasn't set conflicting info\n"));
|
||
|
||
conflictInfo[count].OwningObject = NULL;
|
||
conflictInfo[count].Start = (ULONGLONG)0;
|
||
conflictInfo[count].End = (ULONGLONG)(-1);
|
||
count++;
|
||
|
||
//
|
||
// we daren't continue at risk of looping forever
|
||
//
|
||
break;
|
||
}
|
||
|
||
//
|
||
// reset for next round
|
||
//
|
||
conflictingRange = NULL;
|
||
state.CurrentMinimum = state.Start;
|
||
state.CurrentMaximum = state.End;
|
||
}
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
|
||
if (Arbiter->TransactionInProgress) {
|
||
|
||
status = RtlCopyRangeList(Arbiter->PossibleAllocation, &backupAllocation);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
goto cleanup;
|
||
}
|
||
|
||
RtlFreeRangeList(&backupAllocation);
|
||
}
|
||
|
||
Arbiter->ConflictCallback = savedCallback;
|
||
Arbiter->ConflictCallbackContext = savedContext;
|
||
|
||
*Conflicts = conflictInfo;
|
||
*ConflictCount = count;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cleanup:
|
||
|
||
if (conflictInfo) {
|
||
ExFreePool(conflictInfo);
|
||
}
|
||
|
||
RtlFreeRangeList(Arbiter->PossibleAllocation);
|
||
|
||
if (Arbiter->TransactionInProgress && backedUp) {
|
||
status = RtlCopyRangeList(Arbiter->PossibleAllocation, &backupAllocation);
|
||
RtlFreeRangeList(&backupAllocation);
|
||
}
|
||
|
||
Arbiter->ConflictCallback = savedCallback;
|
||
Arbiter->ConflictCallbackContext = savedContext;
|
||
|
||
*Conflicts = NULL;
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
ArbStartArbiter(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PCM_RESOURCE_LIST StartResources
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called by the driver that implements the arbiter once
|
||
it has been started and knowns what resources it can allocate to its
|
||
children.
|
||
|
||
It will eventually initialize the range lists correctly but for
|
||
now it is just an overloadable place holder as that work is done elsewhere.
|
||
|
||
Parameters:
|
||
|
||
Arbiter - The instance of the arbiter being called.
|
||
|
||
|
||
Return Value:
|
||
|
||
Status code that indicates whether or not the function was successful.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
BOOLEAN
|
||
ArbShareDriverExclusive(
|
||
IN PARBITER_INSTANCE Arbiter,
|
||
IN PARBITER_ALLOCATION_STATE State
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements support for CmResourceShareDriverExclusive disposition
|
||
by overriding conflict if the owner and request share at least one common
|
||
driver.
|
||
|
||
Arguments:
|
||
|
||
Arbiter - The instance data of the arbiter who was called.
|
||
|
||
State - The state of the current arbitration.
|
||
|
||
Return Value:
|
||
|
||
TRUE if the conflict is allowable, false otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PRTL_RANGE current;
|
||
RTL_RANGE_LIST_ITERATOR iterator;
|
||
PDEVICE_OBJECT owner, other;
|
||
ULONG enumeratorNameLength;
|
||
WCHAR enumeratorName[sizeof(REGSTR_KEY_ROOTENUM) / sizeof(WCHAR)];
|
||
NTSTATUS status;
|
||
BOOLEAN isRootEnumerated;
|
||
|
||
PAGED_CODE();
|
||
|
||
isRootEnumerated = FALSE;
|
||
status = IoGetDeviceProperty(
|
||
State->Entry->PhysicalDeviceObject,
|
||
DevicePropertyEnumeratorName,
|
||
sizeof(enumeratorName),
|
||
enumeratorName,
|
||
&enumeratorNameLength);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
if (_wcsicmp(enumeratorName, REGSTR_KEY_ROOTENUM) == 0) {
|
||
|
||
isRootEnumerated = TRUE;
|
||
}
|
||
}
|
||
FOR_ALL_RANGES(Arbiter->PossibleAllocation, &iterator, current) {
|
||
//
|
||
// Only test the overlapping ones
|
||
//
|
||
if (INTERSECT(current->Start, current->End, State->CurrentMinimum, State->CurrentMaximum)) {
|
||
//
|
||
// Check if we should ignore the range because of its attributes
|
||
//
|
||
if (current->Attributes & State->RangeAvailableAttributes) {
|
||
//
|
||
// We DON'T set ok to true because we are just ignoring the range,
|
||
// as RtlFindRange would have and thus it can't be the cause of
|
||
// RtlFindRange failing, so ignoring it can't fix the conflict.
|
||
//
|
||
continue;
|
||
}
|
||
if (State->CurrentAlternative->Descriptor->ShareDisposition != CmResourceShareDriverExclusive &&
|
||
!(current->Attributes & ARBITER_RANGE_SHARE_DRIVER_EXCLUSIVE)) {
|
||
|
||
continue;
|
||
}
|
||
if (!current->Owner) {
|
||
|
||
continue;
|
||
}
|
||
//
|
||
// Special case ROOT enumerated devices.
|
||
//
|
||
if (isRootEnumerated) {
|
||
|
||
status = IoGetDeviceProperty(
|
||
current->Owner,
|
||
DevicePropertyEnumeratorName,
|
||
sizeof(enumeratorName),
|
||
enumeratorName,
|
||
&enumeratorNameLength);
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
if (_wcsicmp(enumeratorName, REGSTR_KEY_ROOTENUM) != 0) {
|
||
|
||
isRootEnumerated = FALSE;
|
||
}
|
||
}
|
||
}
|
||
//
|
||
// If both devices are ROOT enumerated, override the conflict.
|
||
//
|
||
if (isRootEnumerated) {
|
||
|
||
ARB_PRINT(2,
|
||
("Overriding conflict on IRQ %04x for driver %wZ\n",
|
||
(ULONG)State->Start,
|
||
&owner->DriverObject->DriverName
|
||
));
|
||
State->Start=State->CurrentMinimum;
|
||
State->End=State->CurrentMaximum;
|
||
if (State->CurrentAlternative->Descriptor->ShareDisposition == CmResourceShareDriverExclusive) {
|
||
|
||
State->RangeAttributes |= ARBITER_RANGE_SHARE_DRIVER_EXCLUSIVE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
//
|
||
// Check if there is a common driver in the two stacks ignoring the
|
||
// one for the PDO.
|
||
//
|
||
owner = ((PDEVICE_OBJECT)(current->Owner))->AttachedDevice;
|
||
while (owner) {
|
||
|
||
other = (PDEVICE_OBJECT)(State->Entry->PhysicalDeviceObject)->AttachedDevice;
|
||
while (other) {
|
||
|
||
if (owner->DriverObject == other->DriverObject) {
|
||
|
||
ARB_PRINT(2,
|
||
("Overriding conflict on IRQ %04x for driver %wZ\n",
|
||
(ULONG)State->Start,
|
||
&owner->DriverObject->DriverName
|
||
));
|
||
State->Start=State->CurrentMinimum;
|
||
State->End=State->CurrentMaximum;
|
||
if (State->CurrentAlternative->Descriptor->ShareDisposition == CmResourceShareDriverExclusive) {
|
||
|
||
State->RangeAttributes |= ARBITER_RANGE_SHARE_DRIVER_EXCLUSIVE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
other = other->AttachedDevice;
|
||
}
|
||
owner = owner->AttachedDevice;
|
||
}
|
||
}
|
||
}
|
||
//
|
||
// The conflict is still valid
|
||
//
|
||
return FALSE;
|
||
}
|
||
|
||
#if DBG
|
||
VOID
|
||
ArbpIndent(
|
||
IN ULONG Count
|
||
)
|
||
{
|
||
UCHAR spaces[80];
|
||
|
||
ASSERT(Count <= 80);
|
||
|
||
RtlFillMemory(spaces, Count, '*');
|
||
|
||
spaces[Count] = 0;
|
||
|
||
DbgPrint("%s", spaces);
|
||
|
||
}
|
||
#endif
|