ReactOS 0.4.17-dev-684-ga6524ef
range.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Arbitration Library
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Allocation range search core
5 * COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
6 */
7
8/* INCLUDES *******************************************************************/
9
10#include <ntifs.h>
11#include <ndk/rtlfuncs.h>
12#include "arbiter.h"
13
14#define NDEBUG
15#include <debug.h>
16
17/* RANGE WALKER ***************************************************************/
18
35CODE_SEG("PAGE")
36static
37VOID
39 _In_ PARBITER_INSTANCE Arbiter,
40 _Inout_ PARBITER_ALTERNATIVE Alternative)
41{
42 PARBITER_ORDERING Ordering;
44 INT32 Priority = Alternative->Priority;
45 BOOLEAN Preferred;
47
48 PAGED_CODE();
49
52 {
53 Alternative->Priority = ARBITER_PRIORITY_EXHAUSTED;
54 return;
55 }
56
57 Preferred = (Alternative->Descriptor->Option & IO_RESOURCE_PREFERRED) != 0;
58
60 {
61 Ordering = Arbiter->OrderingList.Orderings;
62 }
63 else
64 {
65 /* A fixed alternative fits in exactly one place; it gets a single shot. */
66 if (Alternative->Flags & ARBITER_ALTERNATIVE_FLAG_FIXED)
67 {
68 Alternative->Priority = ARBITER_PRIORITY_EXHAUSTED;
69 return;
70 }
71
72 Index = (Priority < 0) ? (ULONG)(-(Priority + 1)) : (ULONG)(Priority - 1);
73 if (Index >= Arbiter->OrderingList.Count)
74 {
75 Alternative->Priority = Preferred ? ARBITER_PRIORITY_PREFERRED_RESERVED
77 return;
78 }
79 Ordering = &Arbiter->OrderingList.Orderings[Index + 1];
80 }
81
82 End = &Arbiter->OrderingList.Orderings[Arbiter->OrderingList.Count];
83 for (; Ordering < End; ++Ordering)
84 {
85 UINT64 Start, RangeEnd;
86
87 if (Ordering->Start > Alternative->Maximum ||
88 Alternative->Minimum > Ordering->End)
89 {
90 continue; /* No intersection with this alternative's window */
91 }
92
93 Start = max(Alternative->Minimum, Ordering->Start);
94 RangeEnd = min(Alternative->Maximum, Ordering->End);
95
96 if ((RangeEnd - Start + 1) >= Alternative->Length)
97 {
98 INT32 NewPriority = (INT32)(Ordering - Arbiter->OrderingList.Orderings) + 1;
99 Alternative->Priority = Preferred ? -NewPriority : NewPriority;
100 return;
101 }
102 }
103
104 Alternative->Priority = Preferred ? ARBITER_PRIORITY_PREFERRED_RESERVED
106}
107
120CODE_SEG("PAGE")
121static
125{
126 WCHAR Buffer[16];
129 ULONG Length = 0;
130
131 PAGED_CODE();
132
133 if (DeviceObject == NULL)
134 return FALSE;
135
137 sizeof(Buffer), Buffer, &Length)))
138 {
139 return FALSE;
140 }
141
144}
145
161CODE_SEG("PAGE")
162static
165 _In_ PDEVICE_OBJECT DeviceA,
166 _In_ PDEVICE_OBJECT DeviceB)
167{
169
170 PAGED_CODE();
171
172 for (A = DeviceA->AttachedDevice; A != NULL; A = A->AttachedDevice)
173 {
174 for (B = DeviceB->AttachedDevice; B != NULL; B = B->AttachedDevice)
175 {
176 if (A->DriverObject == B->DriverObject)
177 return TRUE;
178 }
179 }
180
181 return FALSE;
182}
183
212CODE_SEG("PAGE")
213static
216 _In_ PARBITER_INSTANCE Arbiter,
218{
219 PARBITER_LIST_ENTRY Entry = ArbState->Entry;
220 PDEVICE_OBJECT Requester;
222 PRTL_RANGE Range;
223 BOOLEAN RequesterIsRoot;
224
225 PAGED_CODE();
226
227 if (Entry == NULL || Entry->PhysicalDeviceObject == NULL ||
228 ArbState->CurrentAlternative == NULL)
229 {
230 return FALSE;
231 }
232
233 Requester = Entry->PhysicalDeviceObject;
234 RequesterIsRoot = ArbpIsRootEnumerated(Requester);
235
236 if (!NT_SUCCESS(RtlGetFirstRange(Arbiter->PossibleAllocation, &Iterator, &Range)))
237 return FALSE;
238
239 while (Range != NULL)
240 {
241 /*
242 * Candidate: overlaps the requested window, is not already made available
243 * by attribute, and either the request or the range is driver-exclusive.
244 */
245 if (Range->Start <= ArbState->CurrentMaximum &&
246 Range->End >= ArbState->CurrentMinimum &&
247 !(Range->Attributes & ArbState->RangeAvailableAttributes) &&
248 (ArbState->CurrentAlternative->Descriptor->ShareDisposition == CmResourceShareDriverExclusive ||
250 Range->Owner != NULL)
251 {
253 BOOLEAN Share = FALSE;
254
255 /* Two root-enumerated devices may share; else only a shared driver. */
256 if (RequesterIsRoot && ArbpIsRootEnumerated(Owner))
257 Share = TRUE;
258 else if (ArbpSharesDriverStack(Requester, Owner))
259 Share = TRUE;
260
261 if (Share)
262 {
263 ArbState->Start = ArbState->CurrentMinimum;
264 ArbState->End = ArbState->CurrentMaximum;
265 if (ArbState->CurrentAlternative->Descriptor->ShareDisposition ==
267 {
268 ArbState->RangeAttributes |= ARBITER_RANGE_SHARED_DRIVER;
269 }
270 return TRUE;
271 }
272 }
273
274 if (!NT_SUCCESS(RtlGetNextRange(&Iterator, &Range, TRUE)))
275 break;
276 }
277
278 return FALSE;
279}
280
313CODE_SEG("PAGE")
314static
317 _In_ PARBITER_INSTANCE Arbiter,
319{
320 PARBITER_LIST_ENTRY Entry = ArbState->Entry;
321 PARBITER_ALTERNATIVE Alternative = ArbState->CurrentAlternative;
323 PRTL_RANGE Range;
324
325 PAGED_CODE();
326
327 if (Arbiter->ResourceType != CmResourceTypeInterrupt)
328 return FALSE;
329
330 if (Entry == NULL || Entry->PhysicalDeviceObject == NULL || Alternative == NULL)
331 return FALSE;
332
333 if (!NT_SUCCESS(RtlGetFirstRange(Arbiter->Allocation, &Iterator, &Range)))
334 return FALSE;
335
336 while (Range != NULL)
337 {
338 if ((PDEVICE_OBJECT)Range->Owner == Entry->PhysicalDeviceObject &&
339 Range->Start >= ArbState->CurrentMinimum &&
340 Range->Start <= ArbState->CurrentMaximum &&
341 Range->End <= ArbState->CurrentMaximum &&
342 (Range->End - Range->Start + 1) >= Alternative->Length)
343 {
344 ArbState->Start = Range->Start;
345 ArbState->End = Range->Start + Alternative->Length - 1;
346 return TRUE;
347 }
348
349 if (!NT_SUCCESS(RtlGetNextRange(&Iterator, &Range, TRUE)))
350 break;
351 }
352
353 return FALSE;
354}
355
376CODE_SEG("PAGE")
378NTAPI
380 _In_ PARBITER_INSTANCE Arbiter,
382{
383 PARBITER_ALTERNATIVE Alternative;
385 UINT64 Minimum, Maximum;
386
387 PAGED_CODE();
388
389 if (ArbState->AlternativeCount == 0)
390 return FALSE;
391
392 for (;;)
393 {
394 /* Advance the alternative we last worked on, or seed all on first entry. */
395 if (ArbState->CurrentAlternative != NULL)
396 {
397 ArbpWritePriority(Arbiter, ArbState->CurrentAlternative);
398 }
399 else
400 {
401 for (Alternative = ArbState->Alternatives;
402 Alternative < &ArbState->Alternatives[ArbState->AlternativeCount];
403 ++Alternative)
404 {
405 Alternative->Priority = ARBITER_PRIORITY_NULL;
406 ArbpWritePriority(Arbiter, Alternative);
407 }
408 }
409
410 /* Pick the best (lowest-priority) alternative. */
411 Lowest = ArbState->Alternatives;
412 for (Alternative = ArbState->Alternatives + 1;
413 Alternative < &ArbState->Alternatives[ArbState->AlternativeCount];
414 ++Alternative)
415 {
416 if (Alternative->Priority < Lowest->Priority)
417 Lowest = Alternative;
418 }
419
421 return FALSE;
422
423 if (Lowest->Priority == ARBITER_PRIORITY_RESERVED ||
425 {
426 /* Final pass: the whole requirement window. */
427 Minimum = Lowest->Minimum;
428 Maximum = Lowest->Maximum;
429 }
430 else
431 {
432 PARBITER_ORDERING Ordering;
433 ULONG Index = (Lowest->Priority < 0) ? (ULONG)(-(Lowest->Priority + 1))
434 : (ULONG)(Lowest->Priority - 1);
435 if (Index >= Arbiter->OrderingList.Count)
436 {
438 continue;
439 }
440 Ordering = &Arbiter->OrderingList.Orderings[Index];
441 Minimum = max(Lowest->Minimum, Ordering->Start);
442 Maximum = min(Lowest->Maximum, Ordering->End);
443 }
444
445 /*
446 * Trim the window so an aligned allocation of the required length is
447 * possible; skip the window entirely if it cannot hold one.
448 */
449 if (Lowest->Length != 0)
450 {
451 UINT64 Alignment = Lowest->Alignment ? Lowest->Alignment : 1;
452 UINT64 LengthMinusOne = Lowest->Length - 1;
453 UINT64 AlignedMax;
454
455 Minimum += Alignment - 1;
456 Minimum -= Minimum % Alignment;
457
458 if (Minimum > Maximum || LengthMinusOne > Maximum - Minimum)
459 {
460 ArbState->CurrentAlternative = Lowest; /* consume this priority */
461 continue;
462 }
463
464 AlignedMax = Maximum - LengthMinusOne;
465 AlignedMax -= AlignedMax % Alignment;
466 if (AlignedMax < Minimum)
467 {
468 ArbState->CurrentAlternative = Lowest; /* no aligned start fits */
469 continue;
470 }
471 Maximum = AlignedMax + LengthMinusOne;
472 }
473 else
474 {
475 Minimum = Lowest->Minimum;
476 Maximum = Lowest->Maximum;
477 }
478
479 if (Minimum != ArbState->CurrentMinimum ||
480 Maximum != ArbState->CurrentMaximum ||
481 ArbState->CurrentAlternative != Lowest)
482 {
483 ArbState->CurrentMinimum = Minimum;
484 ArbState->CurrentMaximum = Maximum;
485 ArbState->CurrentAlternative = Lowest;
486 return TRUE;
487 }
488
489 ArbState->CurrentAlternative = Lowest;
490 }
491}
492
517CODE_SEG("PAGE")
519NTAPI
521 _In_ PARBITER_INSTANCE Arbiter,
523{
524 PARBITER_ALTERNATIVE Alternative = ArbState->CurrentAlternative;
525 ULONG Flags = 0;
527
528 PAGED_CODE();
529
530 if (Alternative == NULL)
531 return FALSE;
532
533 if (ArbState->CurrentMinimum > ArbState->CurrentMaximum)
534 return FALSE;
535
536 if (Alternative->Length == 0)
537 {
538 ArbState->Start = ArbState->CurrentMinimum;
539 ArbState->End = ArbState->CurrentMinimum;
540 return TRUE;
541 }
542
543 /*
544 * Interrupt retention: give the device back its firmware-routed vector rather
545 * than letting the top-down search pick an untranslatable one
546 */
547 if (ArbpReuseOwnedInterrupt(Arbiter, ArbState))
548 return TRUE;
549
550 /* Legacy requests consider preallocated (boot) ranges available. */
551 if (ArbState->Entry != NULL &&
552 (ArbState->Entry->RequestSource == ArbiterRequestLegacyReported ||
553 ArbState->Entry->RequestSource == ArbiterRequestLegacyAssigned))
554 {
555 ArbState->RangeAvailableAttributes |= ARBITER_RANGE_BOOT_ALLOCATED;
556 }
557
558 if (ArbState->Flags & ARBITER_STATE_FLAG_NULL_CONFLICT_OK)
560 if (Alternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED)
562
563 Status = RtlFindRange(Arbiter->PossibleAllocation,
564 ArbState->CurrentMinimum,
565 ArbState->CurrentMaximum,
566 (ULONG)Alternative->Length,
567 (ULONG)(Alternative->Alignment ? Alternative->Alignment : 1),
568 Flags,
569 ArbState->RangeAvailableAttributes,
570 Arbiter->ConflictCallbackContext,
571 Arbiter->ConflictCallback,
572 &ArbState->Start);
573 if (!NT_SUCCESS(Status))
574 {
575 /*
576 * The window is occupied. A CmResourceShareDriverExclusive requirement
577 * can still succeed by sharing the conflicting range with the same driver
578 * or another root-enumerated device
579 *
580 * This matters a lot because HAL reverses quite a bit and marks it this.
581 * This mechanism is how Windows "internally allows this".
582 */
583 if (ArbpShareDriverExclusive(Arbiter, ArbState))
584 return TRUE;
585 if (Arbiter->OverrideConflict != NULL &&
586 Arbiter->OverrideConflict(Arbiter, ArbState))
587 {
588 return TRUE;
589 }
590 return FALSE;
591 }
592
593 ArbState->End = ArbState->Start + Alternative->Length - 1;
594 return TRUE;
595}
596
615CODE_SEG("PAGE")
616VOID
617NTAPI
619 _In_ PARBITER_INSTANCE Arbiter,
621{
623
624 PAGED_CODE();
625
626 if (ArbState->CurrentAlternative != NULL &&
627 (ArbState->CurrentAlternative->Flags & ARBITER_ALTERNATIVE_FLAG_SHARED))
628 {
630 }
631
632 RtlAddRange(Arbiter->PossibleAllocation,
633 ArbState->Start,
634 ArbState->End,
635 ArbState->RangeAttributes,
636 Flags,
637 NULL,
638 ArbState->Entry ? ArbState->Entry->PhysicalDeviceObject : NULL);
639}
640
653CODE_SEG("PAGE")
654VOID
655NTAPI
657 _In_ PARBITER_INSTANCE Arbiter,
659{
660 PAGED_CODE();
661
662 RtlDeleteRange(Arbiter->PossibleAllocation,
663 ArbState->Start,
664 ArbState->End,
665 ArbState->Entry ? ArbState->Entry->PhysicalDeviceObject : NULL);
666}
#define PAGED_CODE()
#define CODE_SEG(...)
static _Out_opt_ PULONGLONG Start
unsigned char BOOLEAN
Definition: actypes.h:127
COMPILER_DEPENDENT_UINT64 UINT64
Definition: actypes.h:131
#define ARBITER_STATE_FLAG_NULL_CONFLICT_OK
Definition: arbiter.h:44
#define ARBITER_ALTERNATIVE_FLAG_SHARED
Definition: arbiter.h:29
#define ARBITER_PRIORITY_NULL
Definition: arbiter.h:22
#define ARBITER_PRIORITY_RESERVED
Definition: arbiter.h:24
#define ARBITER_PRIORITY_EXHAUSTED
Definition: arbiter.h:25
#define ARBITER_PRIORITY_PREFERRED_RESERVED
Definition: arbiter.h:23
#define ARBITER_RANGE_SHARED_DRIVER
Definition: arbiter.h:40
#define ARBITER_ALTERNATIVE_FLAG_FIXED
Definition: arbiter.h:28
#define ARBITER_RANGE_BOOT_ALLOCATED
Definition: arbiter.h:41
LONG NTSTATUS
Definition: precomp.h:26
Definition: ehthrow.cxx:93
Definition: ehthrow.cxx:54
Definition: bufpool.h:45
LPWSTR Name
Definition: desk.c:124
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define A(row, col)
#define B(row, col)
#define RTL_CONSTANT_STRING(s)
Definition: combase.c:35
#define L(x)
Definition: resources.c:13
struct _DEVICE_OBJECT * PDEVICE_OBJECT
union Alignment_ Alignment
Status
Definition: gdiplustypes.h:24
if(dx< 0)
Definition: linetemp.h:194
#define min(a, b)
Definition: monoChain.cc:55
@ Root
Definition: cmtypes.h:261
_Out_writes_bytes_to_opt_ AbsoluteSecurityDescriptorSize PSECURITY_DESCRIPTOR _Inout_ PULONG _Out_writes_bytes_to_opt_ DaclSize PACL _Inout_ PULONG _Out_writes_bytes_to_opt_ SaclSize PACL _Inout_ PULONG _Out_writes_bytes_to_opt_ OwnerSize PSID Owner
Definition: rtlfuncs.h:1629
NTSYSAPI NTSTATUS NTAPI RtlDeleteRange(_Inout_ PRTL_RANGE_LIST RangeList, _In_ ULONGLONG Start, _In_ ULONGLONG End, _In_ PVOID Owner)
NTSYSAPI NTSTATUS NTAPI RtlAddRange(_Inout_ PRTL_RANGE_LIST RangeList, _In_ ULONGLONG Start, _In_ ULONGLONG End, _In_ UCHAR Attributes, _In_ ULONG Flags, _In_opt_ PVOID UserData, _In_opt_ PVOID Owner)
NTSYSAPI NTSTATUS NTAPI RtlFindRange(_In_ PRTL_RANGE_LIST RangeList, _In_ ULONGLONG Minimum, _In_ ULONGLONG Maximum, _In_ ULONG Length, _In_ ULONG Alignment, _In_ ULONG Flags, _In_ UCHAR AttributeAvailableMask, _In_opt_ PVOID Context, _In_opt_ PRTL_CONFLICT_RANGE_CALLBACK Callback, _Out_ PULONGLONG Start)
NTSYSAPI NTSTATUS NTAPI RtlGetNextRange(_Inout_ PRTL_RANGE_LIST_ITERATOR Iterator, _Outptr_ PRTL_RANGE *Range, _In_ BOOLEAN MoveForwards)
NTSYSAPI NTSTATUS NTAPI RtlGetFirstRange(_In_ PRTL_RANGE_LIST RangeList, _Out_ PRTL_RANGE_LIST_ITERATOR Iterator, _Outptr_ PRTL_RANGE *Range)
#define RTL_RANGE_LIST_ADD_IF_CONFLICT
Definition: rtltypes.h:81
#define RTL_RANGE_LIST_ADD_SHARED
Definition: rtltypes.h:82
#define RTL_RANGE_LIST_NULL_CONFLICT_OK
Definition: rtltypes.h:90
#define RTL_RANGE_LIST_SHARED_OK
Definition: rtltypes.h:89
#define _Inout_
Definition: no_sal2.h:162
#define _In_
Definition: no_sal2.h:158
NTSYSAPI VOID NTAPI RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
NTSYSAPI BOOLEAN NTAPI RtlEqualUnicodeString(PUNICODE_STRING String1, PUNICODE_STRING String2, BOOLEAN CaseInSensitive)
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
short WCHAR
Definition: pedump.c:58
NTSTATUS NTAPI IoGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject, IN DEVICE_REGISTRY_PROPERTY DeviceProperty, IN ULONG BufferLength, OUT PVOID PropertyBuffer, OUT PULONG ResultLength)
Definition: pnpmgr.c:1382
BOOLEAN NTAPI ArbiterLibGetNextAllocationRange(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALLOCATION_STATE ArbState)
Moves the working window to the next candidate range, walking the entry's alternatives in priority or...
Definition: range.c:379
VOID NTAPI ArbiterLibBacktrackAllocation(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALLOCATION_STATE ArbState)
Undoes the last AddAllocation performed for this entry.
Definition: range.c:656
BOOLEAN NTAPI ArbiterLibFindSuitableRange(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALLOCATION_STATE ArbState)
Finds a free range of the current candidate window in the arbiter's tentative allocation list.
Definition: range.c:520
static VOID ArbpWritePriority(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALTERNATIVE Alternative)
Writes an alternative's priority to the next ordering-list range it can be satisfied from.
Definition: range.c:38
static BOOLEAN ArbpSharesDriverStack(_In_ PDEVICE_OBJECT DeviceA, _In_ PDEVICE_OBJECT DeviceB)
Determines whether a common driver is loaded on both device stacks, above the physical device objects...
Definition: range.c:164
static BOOLEAN ArbpShareDriverExclusive(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALLOCATION_STATE ArbState)
Attempts last-chance sharing for a CmResourceShareDriverExclusive requirement whose window RtlFindRan...
Definition: range.c:215
VOID NTAPI ArbiterLibAddAllocation(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALLOCATION_STATE ArbState)
Records the chosen placement in the arbiter's tentative allocation list, owned by the requesting devi...
Definition: range.c:618
static BOOLEAN ArbpReuseOwnedInterrupt(_In_ PARBITER_INSTANCE Arbiter, _Inout_ PARBITER_ALLOCATION_STATE ArbState)
Hands a device back its own already routed IRQ instead of searching for a fresh one,...
Definition: range.c:316
static BOOLEAN ArbpIsRootEnumerated(_In_ PDEVICE_OBJECT DeviceObject)
Determines whether a device is enumerated by the root enumerator.
Definition: range.c:123
#define CmResourceTypeInterrupt
Definition: restypes.h:105
Entry
Definition: section.c:5216
Definition: iotypes.h:4633
UINT64 Start
Definition: arbiter.h:81
ULONGLONG End
Definition: rtltypes.h:1497
ULONGLONG Start
Definition: rtltypes.h:1496
UCHAR Attributes
Definition: rtltypes.h:1500
PVOID Owner
Definition: rtltypes.h:1499
#define max(a, b)
Definition: svc.c:63
int32_t INT32
Definition: typedefs.h:58
#define NTAPI
Definition: typedefs.h:36
uint32_t ULONG
Definition: typedefs.h:59
_In_ WDFCHILDLIST _In_ PWDF_CHILD_LIST_ITERATOR Iterator
Definition: wdfchildlist.h:656
_In_ WDFCOLLECTION _In_ ULONG Index
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2061
_In_ WDFINTERRUPT _In_ WDF_INTERRUPT_POLICY _In_ WDF_INTERRUPT_PRIORITY Priority
Definition: wdfinterrupt.h:655
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
@ CmResourceShareDriverExclusive
Definition: cmtypes.h:242
@ ArbiterRequestLegacyAssigned
Definition: iotypes.h:4619
@ ArbiterRequestLegacyReported
Definition: iotypes.h:4617
@ DevicePropertyEnumeratorName
Definition: iotypes.h:1210
#define IO_RESOURCE_PREFERRED