/*++ Copyright (c) 1997 Microsoft Corporation 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 - BUGBUG - explain better 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 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_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