/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: arbiter.h Abstract: This module contains support routines for the Pnp resource arbiters. Author: Andrew Thornton (andrewth) 1-April-1997 Environment: Kernel mode --*/ #ifndef _ARBITER_ #define _ARBITER_ #if !defined(MAXULONGLONG) #define MAXULONGLONG ((ULONGLONG)-1) #endif #if ARB_DBG // // Debug print level: // -1 = no messages // 0 = vital messages only // 1 = call trace // 2 = verbose messages // extern LONG ArbDebugLevel; #define ARB_PRINT(Level, Message) \ if (Level <= ArbDebugLevel) DbgPrint Message #define ARB_INDENT(Level, Count) \ if (Level < ArbDebugLevel) ArbpIndent(Count) #else #define ARB_PRINT(Level, Message) #define ARB_INDENT(Level, Count) #endif // ARB_DBG // // The ARBITER_ORDRING_LIST abstract data type // typedef struct _ARBITER_ORDERING { ULONGLONG Start; ULONGLONG End; } ARBITER_ORDERING, *PARBITER_ORDERING; typedef struct _ARBITER_ORDERING_LIST { // // The number of valid entries in the array // USHORT Count; // // The maximum number of entries that can fit in the Ordering buffer // USHORT Maximum; // // Array of orderings // PARBITER_ORDERING Orderings; } ARBITER_ORDERING_LIST, *PARBITER_ORDERING_LIST; NTSTATUS ArbInitializeOrderingList( IN OUT PARBITER_ORDERING_LIST List ); VOID ArbFreeOrderingList( IN OUT PARBITER_ORDERING_LIST List ); NTSTATUS ArbCopyOrderingList( OUT PARBITER_ORDERING_LIST Destination, IN PARBITER_ORDERING_LIST Source ); NTSTATUS ArbAddOrdering( OUT PARBITER_ORDERING_LIST List, IN ULONGLONG Start, IN ULONGLONG End ); NTSTATUS ArbPruneOrdering( IN OUT PARBITER_ORDERING_LIST OrderingList, IN ULONGLONG Start, IN ULONGLONG End ); // // ULONGLONG // ALIGN_ADDRESS_DOWN( // ULONGLONG address, // ULONG alignment // ); // // This aligns address to the previously correctly aligned value // #define ALIGN_ADDRESS_DOWN(address, alignment) \ ((address) & ~((ULONGLONG)alignment - 1)) // // ULONGLONG // ALIGN_ADDRESS_UP( // ULONGLONG address, // ULONG alignment // ); // // This aligns address to the next correctly aligned value // #define ALIGN_ADDRESS_UP(address, alignment) \ (ALIGN_ADDRESS_DOWN( (address + alignment - 1), alignment)) #define LENGTH_OF(_start, _end) \ ((_end) - (_start) + 1) // // This indicates that the alternative can coexist with shared resources and // should be added to the range lists shared // #define ARBITER_ALTERNATIVE_FLAG_SHARED 0x00000001 // // This indicates that the request if for a specific range with no alternatives. // ie (End - Start + 1 == Length) eg port 60-60 L1 A1 // #define ARBITER_ALTERNATIVE_FLAG_FIXED 0x00000002 // // This indicates that request is invalid // #define ARBITER_ALTERNATIVE_FLAG_INVALID 0x00000004 typedef struct _ARBITER_ALTERNATIVE { // // The minimum acceptable start value from the requirement descriptor // ULONGLONG Minimum; // // The maximum acceptable end value from the requirement descriptor // ULONGLONG Maximum; // // The length from the requirement descriptor // ULONG Length; // // The alignment from the requirement descriptor // ULONG Alignment; // // Priority index - see comments below // LONG Priority; // // Flags - ARBITER_ALTERNATIVE_FLAG_SHARED - indicates the current // requirement was for a shared resource. // ARBITER_ALTERNATIVE_FLAG_FIXED - indicates the current // requirement is for a specific resource (eg ports 220-230 and // nothing else) // ULONG Flags; // // Descriptor - the descriptor describing this alternative // PIO_RESOURCE_DESCRIPTOR Descriptor; // // Packing... // ULONG Reserved[3]; } ARBITER_ALTERNATIVE, *PARBITER_ALTERNATIVE; /* 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 range left. */ // // The least significant 16 bits are reserved for the base arbitration code // the most significant are arbiter specific // #define ARBITER_STATE_FLAG_RETEST 0x0001 #define ARBITER_STATE_FLAG_BOOT 0x0002 #define ARBITER_STATE_FLAG_CONFLICT 0x0004 #define ARBITER_STATE_FLAG_NULL_CONFLICT_OK 0x0008 typedef struct _ARBITER_ALLOCATION_STATE { // // The current value being considered as a possible start value // ULONGLONG Start; // // The current value being considered as a possible end value // ULONGLONG End; // // The values currently being considered as the Minimum and Maximum (this is // different because the prefered orderings can restrict the ranges where // we can allocate) // ULONGLONG CurrentMinimum; ULONGLONG CurrentMaximum; // // The entry in the arbitration list containing this request. // PARBITER_LIST_ENTRY Entry; // // The alternative currently being considered // PARBITER_ALTERNATIVE CurrentAlternative; // // The number of alternatives in the Alternatives array // ULONG AlternativeCount; // // The arbiters representation of the alternatives being considered // PARBITER_ALTERNATIVE Alternatives; // // Flags - ARBITER_STATE_FLAG_RETEST - indicates that we are in a retest // operation not a test. // ARBITER_STATE_FLAG_BOOT - indicates we are in a boot allocation // operation not a test. // USHORT Flags; // // RangeAttributes - these are logically ORed in to the attributes for all // ranges added to the range list. // UCHAR RangeAttributes; // // Ranges that are to be considered available // UCHAR RangeAvailableAttributes; // // Space for the arbiter to use as it wishes // ULONG_PTR WorkSpace; } ARBITER_ALLOCATION_STATE, *PARBITER_ALLOCATION_STATE; typedef struct _ARBITER_INSTANCE ARBITER_INSTANCE, *PARBITER_INSTANCE; typedef NTSTATUS (*PARBITER_UNPACK_REQUIREMENT) ( IN PIO_RESOURCE_DESCRIPTOR Descriptor, OUT PULONGLONG Minimum, OUT PULONGLONG Maximum, OUT PULONG Length, OUT PULONG Alignment ); typedef NTSTATUS (*PARBITER_PACK_RESOURCE) ( IN PIO_RESOURCE_DESCRIPTOR Requirement, IN ULONGLONG Start, OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor ); typedef NTSTATUS (*PARBITER_UNPACK_RESOURCE) ( IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor, OUT PULONGLONG Start, OUT PULONG Length ); typedef LONG (*PARBITER_SCORE_REQUIREMENT) ( IN PIO_RESOURCE_DESCRIPTOR Descriptor ); typedef NTSTATUS (*PARBITER_PREPROCESS_ENTRY)( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE Entry ); typedef NTSTATUS (*PARBITER_ALLOCATE_ENTRY)( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE Entry ); typedef NTSTATUS (*PARBITER_TEST_ALLOCATION)( IN PARBITER_INSTANCE Arbiter, IN OUT PLIST_ENTRY ArbitrationList ); typedef NTSTATUS (*PARBITER_COMMIT_ALLOCATION)( IN PARBITER_INSTANCE Arbiter ); typedef NTSTATUS (*PARBITER_ROLLBACK_ALLOCATION)( IN PARBITER_INSTANCE Arbiter ); typedef NTSTATUS (*PARBITER_RETEST_ALLOCATION)( IN PARBITER_INSTANCE Arbiter, IN OUT PLIST_ENTRY ArbitrationList ); typedef NTSTATUS (*PARBITER_BOOT_ALLOCATION)( IN PARBITER_INSTANCE Arbiter, IN OUT PLIST_ENTRY ArbitrationList ); typedef NTSTATUS (*PARBITER_ADD_RESERVED)( IN PARBITER_INSTANCE Arbiter, IN PIO_RESOURCE_DESCRIPTOR Requirement OPTIONAL, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Resource OPTIONAL ); typedef BOOLEAN (*PARBITER_GET_NEXT_ALLOCATION_RANGE)( PARBITER_INSTANCE Arbiter, PARBITER_ALLOCATION_STATE State ); typedef BOOLEAN (*PARBITER_FIND_SUITABLE_RANGE)( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); typedef VOID (*PARBITER_ADD_ALLOCATION)( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); typedef VOID (*PARBITER_BACKTRACK_ALLOCATION)( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); typedef BOOLEAN (*PARBITER_OVERRIDE_CONFLICT)( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); typedef NTSTATUS (*PARBITER_QUERY_ARBITRATE)( IN PARBITER_INSTANCE Arbiter, IN PLIST_ENTRY ArbitrationList ); typedef NTSTATUS (*PARBITER_QUERY_CONFLICT)( IN PARBITER_INSTANCE Arbiter, IN PDEVICE_OBJECT PhysicalDeviceObject, IN PIO_RESOURCE_DESCRIPTOR ConflictingResource, OUT PULONG ConflictCount, OUT PARBITER_CONFLICT_INFO *Conflicts ); typedef NTSTATUS (*PARBITER_START_ARBITER)( IN PARBITER_INSTANCE Arbiter, IN PCM_RESOURCE_LIST StartResources ); // // Attributes for the ranges // #define ARBITER_RANGE_BOOT_ALLOCATED 0x01 #define ARBITER_RANGE_SHARE_DRIVER_EXCLUSIVE 0x02 #define ARBITER_RANGE_ALIAS 0x10 #define ARBITER_RANGE_POSITIVE_DECODE 0x20 #define INITIAL_ALLOCATION_STATE_SIZE PAGE_SIZE #define ARBITER_INSTANCE_SIGNATURE 'sbrA' typedef struct _ARBITER_INSTANCE { // // Signature - must be ARBITER_INSTANCE_SIGNATURE // ULONG Signature; // // Synchronisation lock // PKEVENT MutexEvent; // // The name of this arbiter - used for debugging and registry storage // PWSTR Name; // // The resource type this arbiter arbitrates. // CM_RESOURCE_TYPE ResourceType; // // Pointer to a pool allocated range list which contains the current // allocation // PRTL_RANGE_LIST Allocation; // // Pointer to a pool allocated range list which contains the allocation // under considetation. This is set by test allocation. // PRTL_RANGE_LIST PossibleAllocation; // // The order in which these resources should be allocated. Taken from the // HKLM\System\CurrentControlSet\Control\SystemResources\AssignmentOrdering // key and modified based on the reserved resources. // ARBITER_ORDERING_LIST OrderingList; // // The resources that should be reserved (not allocated until absolutley // necessary) // ARBITER_ORDERING_LIST ReservedList; // // The reference count of the number of entities that are using the // ARBITER_INTERFACE associated with this instance. // LONG ReferenceCount; // // The ARBITER_INTERFACE associated with this instance. // PARBITER_INTERFACE Interface; // // The size in bytes of the currently allocated AllocationStack // ULONG AllocationStackMaxSize; // // A pointer to an array of ARBITER_ALLOCATION_STATE entries encapsulating // the state of the current arbitration // PARBITER_ALLOCATION_STATE AllocationStack; // // Required helper function dispatches - these functions must always be // provided // PARBITER_UNPACK_REQUIREMENT UnpackRequirement; PARBITER_PACK_RESOURCE PackResource; PARBITER_UNPACK_RESOURCE UnpackResource; PARBITER_SCORE_REQUIREMENT ScoreRequirement; // // Main arbiter action dispatches // PARBITER_TEST_ALLOCATION TestAllocation; OPTIONAL PARBITER_RETEST_ALLOCATION RetestAllocation; OPTIONAL PARBITER_COMMIT_ALLOCATION CommitAllocation; OPTIONAL PARBITER_ROLLBACK_ALLOCATION RollbackAllocation; OPTIONAL PARBITER_BOOT_ALLOCATION BootAllocation; OPTIONAL PARBITER_QUERY_ARBITRATE QueryArbitrate; OPTIONAL PARBITER_QUERY_CONFLICT QueryConflict; OPTIONAL PARBITER_ADD_RESERVED AddReserved; OPTIONAL PARBITER_START_ARBITER StartArbiter; OPTIONAL // // Optional helper functions // PARBITER_PREPROCESS_ENTRY PreprocessEntry; OPTIONAL PARBITER_ALLOCATE_ENTRY AllocateEntry; OPTIONAL PARBITER_GET_NEXT_ALLOCATION_RANGE GetNextAllocationRange; OPTIONAL PARBITER_FIND_SUITABLE_RANGE FindSuitableRange; OPTIONAL PARBITER_ADD_ALLOCATION AddAllocation; OPTIONAL PARBITER_BACKTRACK_ALLOCATION BacktrackAllocation; OPTIONAL PARBITER_OVERRIDE_CONFLICT OverrideConflict; OPTIONAL // // Debugging support // BOOLEAN TransactionInProgress; // // Arbiter specific extension - can be used to store extra arbiter specific // information // PVOID Extension; // // The bus device we arbitrate for // PDEVICE_OBJECT BusDeviceObject; // // Callback and context for RtlFindRange/RtlIsRangeAvailable to allow // complex conflicts // PVOID ConflictCallbackContext; PRTL_CONFLICT_RANGE_CALLBACK ConflictCallback; } ARBITER_INSTANCE, *PARBITER_INSTANCE; // // Lock primitives that leave us at PASSIVE_LEVEL after acquiring the lock. // (A FAST_MUTEX or CriticalRegion leave us at APC level and some people (ACPI) // need to be at passive level in their arbiter) // #define ArbAcquireArbiterLock(_Arbiter) \ KeWaitForSingleObject( (_Arbiter)->MutexEvent, Executive, KernelMode, FALSE, NULL ) #define ArbReleaseArbiterLock(_Arbiter) \ KeSetEvent( (_Arbiter)->MutexEvent, 0, FALSE ) // // Iteration macros // // // Control macro (used like a for loop) which iterates over all entries in // a standard doubly linked list. Head is the list head and the entries are of // type Type. A member called ListEntry is assumed to be the LIST_ENTRY // structure linking the entries together. Current contains a pointer to each // entry in turn. // #define FOR_ALL_IN_LIST(Type, Head, Current) \ for((Current) = CONTAINING_RECORD((Head)->Flink, Type, ListEntry); \ (Head) != &(Current)->ListEntry; \ (Current) = CONTAINING_RECORD((Current)->ListEntry.Flink, \ Type, \ ListEntry) \ ) // // Similar to the above only iteration is over an array of length _Size. // #define FOR_ALL_IN_ARRAY(_Array, _Size, _Current) \ for ( (_Current) = (_Array); \ (_Current) < (_Array) + (_Size); \ (_Current)++ ) // // As above only iteration begins with the entry _Current // #define FOR_REST_IN_ARRAY(_Array, _Size, _Current) \ for ( ; \ (_Current) < (_Array) + (_Size); \ (_Current)++ ) // // BOOLEAN // INTERSECT( // ULONGLONG s1, // ULONGLONG e1, // ULONGLONG s2, // ULONGLONG e2 // ); // // Determines if the ranges s1-e1 and s2-e2 intersect // #define INTERSECT(s1,e1,s2,e2) \ !( ((s1) < (s2) && (e1) < (s2)) \ ||((s2) < (s1) && (e2) < (s1)) ) // // ULONGLONG // INTERSECT_SIZE( // ULONGLONG s1, // ULONGLONG e1, // ULONGLONG s2, // ULONGLONG e2 // ); // // Returns the size of the intersection of s1-e1 and s2-e2, undefined if they // don't intersect // #define INTERSECT_SIZE(s1,e1,s2,e2) \ ( __min((e1),(e2)) - __max((s1),(s2)) + 1) #define LEGACY_REQUEST(_Entry) \ ((_Entry)->RequestSource == ArbiterRequestLegacyReported || \ (_Entry)->RequestSource == ArbiterRequestLegacyAssigned) #define PNP_REQUEST(_Entry) \ ((_Entry)->RequestSource == ArbiterRequestPnpDetected || \ (_Entry)->RequestSource == ArbiterRequestPnpEnumerated) // // Priorities used in ArbGetNextAllocationRange // #define ARBITER_PRIORITY_NULL 0 #define ARBITER_PRIORITY_PREFERRED_RESERVED (MAXLONG-2) #define ARBITER_PRIORITY_RESERVED (MAXLONG-1) #define ARBITER_PRIORITY_EXHAUSTED (MAXLONG) typedef NTSTATUS (*PARBITER_TRANSLATE_ALLOCATION_ORDER)( OUT PIO_RESOURCE_DESCRIPTOR TranslatedDescriptor, IN PIO_RESOURCE_DESCRIPTOR RawDescriptor ); // // Common arbiter routines // NTSTATUS ArbInitializeArbiterInstance( OUT PARBITER_INSTANCE Arbiter, IN PDEVICE_OBJECT BusDevice, IN CM_RESOURCE_TYPE ResourceType, IN PWSTR Name, IN PWSTR OrderingName, IN PARBITER_TRANSLATE_ALLOCATION_ORDER TranslateOrdering ); VOID ArbDeleteArbiterInstance( IN PARBITER_INSTANCE Arbiter ); NTSTATUS ArbArbiterHandler( IN PVOID Context, IN ARBITER_ACTION Action, IN OUT PARBITER_PARAMETERS Params ); NTSTATUS ArbTestAllocation( IN PARBITER_INSTANCE Arbiter, IN OUT PLIST_ENTRY ArbitrationList ); NTSTATUS ArbRetestAllocation( IN PARBITER_INSTANCE Arbiter, IN OUT PLIST_ENTRY ArbitrationList ); NTSTATUS ArbCommitAllocation( PARBITER_INSTANCE Arbiter ); NTSTATUS ArbRollbackAllocation( PARBITER_INSTANCE Arbiter ); NTSTATUS ArbAddReserved( IN PARBITER_INSTANCE Arbiter, IN PIO_RESOURCE_DESCRIPTOR Requirement OPTIONAL, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Resource OPTIONAL ); NTSTATUS ArbPreprocessEntry( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); NTSTATUS ArbAllocateEntry( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); NTSTATUS ArbSortArbitrationList( IN OUT PLIST_ENTRY ArbitrationList ); VOID ArbConfirmAllocation( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); BOOLEAN ArbOverrideConflict( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); NTSTATUS ArbQueryConflict( IN PARBITER_INSTANCE Arbiter, IN PDEVICE_OBJECT PhysicalDeviceObject, IN PIO_RESOURCE_DESCRIPTOR ConflictingResource, OUT PULONG ConflictCount, OUT PARBITER_CONFLICT_INFO *Conflicts ); VOID ArbBacktrackAllocation( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); BOOLEAN ArbGetNextAllocationRange( PARBITER_INSTANCE Arbiter, PARBITER_ALLOCATION_STATE State ); BOOLEAN ArbFindSuitableRange( PARBITER_INSTANCE Arbiter, PARBITER_ALLOCATION_STATE State ); VOID ArbAddAllocation( IN PARBITER_INSTANCE Arbiter, IN PARBITER_ALLOCATION_STATE State ); NTSTATUS ArbBootAllocation( IN PARBITER_INSTANCE Arbiter, IN OUT PLIST_ENTRY ArbitrationList ); NTSTATUS ArbStartArbiter( IN PARBITER_INSTANCE Arbiter, IN PCM_RESOURCE_LIST StartResources ); NTSTATUS ArbBuildAssignmentOrdering( IN OUT PARBITER_INSTANCE Arbiter, IN PWSTR AllocationOrderName, IN PWSTR ReservedResourcesName, IN PARBITER_TRANSLATE_ALLOCATION_ORDER Translate OPTIONAL ); #if ARB_DBG VOID ArbpIndent( ULONG Count ); #endif // DBG #endif