/*++

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 - 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


