ReactOS 0.4.17-dev-650-gd0e71de
ordering.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS Arbitration Library
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Ordering lists and the registry assignment ordering
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#define ARBITER_ORDERING_GRANULARITY 16
18#define ARBITER_ORDERING_LIMIT 1024
19
20#define ARBP_POLICY_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Arbiters\\"
21#define ARBP_LONGEST_SUBKEY L"ReservedResources"
22
23/* ORDERING LISTS *************************************************************/
24
25/* Set up an empty ordering array at its starting capacity. */
26CODE_SEG("PAGE")
27static
30 _Out_ PARBITER_ORDERING_LIST OrderingList)
31{
32 PAGED_CODE();
33
34 OrderingList->Orderings = ExAllocatePoolWithTag(PagedPool,
37 if (OrderingList->Orderings == NULL)
38 {
39 OrderingList->Count = 0;
40 OrderingList->Maximum = 0;
42 }
43
44 OrderingList->Count = 0;
45 OrderingList->Maximum = ARBITER_ORDERING_GRANULARITY;
46 return STATUS_SUCCESS;
47}
48
49CODE_SEG("PAGE")
50VOID
54{
55 PAGED_CODE();
56
57 if (OrderingList->Orderings != NULL)
58 ExFreePoolWithTag(OrderingList->Orderings, TAG_ARBITER);
59
60 OrderingList->Orderings = NULL;
61 OrderingList->Count = 0;
62 OrderingList->Maximum = 0;
63}
64
65/* Append one [Start, End] window (both bounds inclusive) at the tail
66 * (= lowest preference so far). */
67CODE_SEG("PAGE")
73 _In_ UINT64 End)
74{
75 PAGED_CODE();
76
77 if (End < Start)
79
80 /* Double the array capacity when it fills up, up to the sanity cap. */
81 if (OrderingList->Count == OrderingList->Maximum)
82 {
83 ULONG NewMaximum;
84 PARBITER_ORDERING NewArray;
85
86 NewMaximum = OrderingList->Maximum ? (ULONG)OrderingList->Maximum * 2
88 if (NewMaximum > ARBITER_ORDERING_LIMIT)
90
92 NewMaximum * sizeof(ARBITER_ORDERING),
94 if (NewArray == NULL)
96
97 if (OrderingList->Orderings != NULL)
98 {
99 RtlCopyMemory(NewArray, OrderingList->Orderings,
100 OrderingList->Count * sizeof(ARBITER_ORDERING));
101 ExFreePoolWithTag(OrderingList->Orderings, TAG_ARBITER);
102 }
103
104 OrderingList->Maximum = (UINT16)NewMaximum;
105 OrderingList->Orderings = NewArray;
106 }
107
108 OrderingList->Orderings[OrderingList->Count].Start = Start;
109 OrderingList->Orderings[OrderingList->Count].End = End;
110 OrderingList->Count++;
111 return STATUS_SUCCESS;
112}
113
114/* REGISTRY-DESCRIBED RANGES **************************************************/
115
116/*
117 * An arbiter refines its allocation policy from HKLM\...\Control\Arbiters.
118 * Each value there is a REG_RESOURCE_REQUIREMENTS_LIST whose descriptors give
119 * [MinimumAddress, MaximumAddress] ranges, filtered by the arbiter's resource
120 * This can get written to by chipset drivers.
121 */
122
123typedef VOID
125 _In_ PARBITER_INSTANCE Arbiter,
128 _In_ ULONGLONG End);
129
130/* Open one subkey of the Arbiters policy key by composing its full path. */
131CODE_SEG("PAGE")
132static
137{
138 WCHAR PathBuffer[sizeof(ARBP_POLICY_ROOT ARBP_LONGEST_SUBKEY) / sizeof(WCHAR)];
139 UNICODE_STRING KeyPath;
142
143 PAGED_CODE();
144
145 RtlInitEmptyUnicodeString(&KeyPath, PathBuffer, sizeof(PathBuffer));
147 if (NT_SUCCESS(Status))
149 if (!NT_SUCCESS(Status))
150 return Status;
151
154 return ZwOpenKey(KeyHandle, KEY_READ, &ObjectAttributes);
155}
156
157/* Read one value into a freshly-allocated KEY_VALUE_FULL_INFORMATION. */
158CODE_SEG("PAGE")
159static
165{
166 UNICODE_STRING NameString;
168 ULONG Size = 0;
171
172 PAGED_CODE();
173
174 RtlInitUnicodeString(&NameString, ValueName);
175
176 /* Size probe: only a buffer-size result means the value is readable. */
177 Status = ZwQueryValueKey(KeyHandle, &NameString, KeyValueFullInformationAlign64,
178 NULL, 0, &Size);
181
183 if (Buffer == NULL)
185
186 Status = ZwQueryValueKey(KeyHandle, &NameString, KeyValueFullInformationAlign64,
188
189 if (NT_SUCCESS(Status) &&
190 ((ULONGLONG)Buffer->DataOffset + Buffer->DataLength > ResultLength))
191 {
193 }
194
195 if (!NT_SUCCESS(Status))
196 {
198 return Status;
199 }
200
201 *Value = Buffer;
202 return STATUS_SUCCESS;
203}
204
205/* Does a policy descriptor apply to this arbiter's resource type? */
206CODE_SEG("PAGE")
207static
210 _In_ PARBITER_INSTANCE Arbiter,
212{
213 PAGED_CODE();
214
215 if (DescriptorType == (UCHAR)Arbiter->ResourceType)
216 return TRUE;
217
218 /* Large-memory descriptors also satisfy the plain memory arbiter. */
220 Arbiter->ResourceType == CmResourceTypeMemory);
221}
222
223/*
224 * Walk the policy ranges stored under HKLM\...\Control\Arbiters<Subkey>,
225 * This is used by machine-specific policy tables.
226 */
227CODE_SEG("PAGE")
228static
231 _In_ PARBITER_INSTANCE Arbiter,
232 _In_ PCWSTR Subkey,
236{
240
241 PAGED_CODE();
242
244 return STATUS_SUCCESS;
245
247
248 /* Follow a REG_SZ indirection to the named sibling value. */
249 if (NT_SUCCESS(Status) && Value->Type == REG_SZ)
250 {
251 PWSTR TargetName = (PWSTR)((PUCHAR)Value + Value->DataOffset);
252 ULONG TargetChars = Value->DataLength / sizeof(WCHAR);
254
255 if (TargetChars != 0 &&
256 TargetName[TargetChars - 1] == UNICODE_NULL &&
258 {
260 Value = Target;
261 }
262 }
263
264 if (NT_SUCCESS(Status) &&
266 Value->DataLength >= sizeof(IO_RESOURCE_REQUIREMENTS_LIST))
267 {
269 PIO_RESOURCE_LIST Alternative;
270 PUCHAR DataEnd;
271 ULONG Index;
272
273 Requirements = (PIO_RESOURCE_REQUIREMENTS_LIST)((PUCHAR)Value + Value->DataOffset);
274 DataEnd = (PUCHAR)Requirements + Value->DataLength;
275 Alternative = &Requirements->List[0];
276
277 for (Index = 0; Index < Alternative->Count; ++Index)
278 {
280
281 /* Never trust the stored Count past the actual value data. */
282 if ((PUCHAR)(Descriptor + 1) > DataEnd)
283 break;
284
285 if (ArbpDescriptorMatchesArbiter(Arbiter, Descriptor->Type))
286 {
287 Callback(Arbiter, Context,
288 (ULONGLONG)Descriptor->u.Generic.MinimumAddress.QuadPart,
289 (ULONGLONG)Descriptor->u.Generic.MaximumAddress.QuadPart);
290 }
291 }
292 }
293
294 if (Value != NULL)
297 return STATUS_SUCCESS;
298}
299
300/* Prepend a registry-described preferred range ahead of the full-range default. */
301CODE_SEG("PAGE")
302static
303VOID
304NTAPI
306 _In_ PARBITER_INSTANCE Arbiter,
309 _In_ ULONGLONG End)
310{
311 PAGED_CODE();
313 ArbiterLibAddOrdering(&Arbiter->OrderingList, Start, End);
314}
315
316/*
317 * Build the arbiter's allocation ordering. The registry-described preferred
318 * ranges (AllocationOrder<AllocationOrderName>) the engine walks the
319 * list in index order, so index 0 is the most-preferred window. If one isnt
320 * found its followed by a full-range fallback so the arbiter can always search the whole space
321 *
322 * Both ordering lists are (re)created here: callers may rebuild an existing
323 * ordering against a different policy table (see pcix ario_ApplyBrokenVideoHack).
324 */
325CODE_SEG("PAGE")
327NTAPI
330 _In_ PCWSTR AllocationOrderName,
331 _In_ PCWSTR ReservedResourcesName,
332 _In_opt_ PARB_TRANSLATE_ORDERING TranslateOrderingFunction)
333{
335
336 PAGED_CODE();
337
338 UNREFERENCED_PARAMETER(ReservedResourcesName);
339 UNREFERENCED_PARAMETER(TranslateOrderingFunction);
340
341 ArbiterLibFreeOrderingList(&Arbiter->OrderingList);
342 ArbiterLibFreeOrderingList(&Arbiter->ReservedList);
343
344 Status = ArbpCreateOrderingList(&Arbiter->OrderingList);
345 if (!NT_SUCCESS(Status))
346 return Status;
347 Status = ArbpCreateOrderingList(&Arbiter->ReservedList);
348 if (!NT_SUCCESS(Status))
349 {
350 ArbiterLibFreeOrderingList(&Arbiter->OrderingList);
351 return Status;
352 }
353
354 ArbpForEachRegistryRange(Arbiter, L"AllocationOrder", AllocationOrderName,
356
357 return ArbiterLibAddOrdering(&Arbiter->OrderingList, 0, 0xFFFFFFFFFFFFFFFFULL);
358}
#define PAGED_CODE()
#define CODE_SEG(...)
static _Out_opt_ PULONGLONG Start
#define VOID
Definition: acefi.h:82
unsigned short UINT16
Definition: actypes.h:129
unsigned char BOOLEAN
Definition: actypes.h:127
COMPILER_DEPENDENT_UINT64 UINT64
Definition: actypes.h:131
NTSTATUS(NTAPI * PARB_TRANSLATE_ORDERING)(_Out_ PIO_RESOURCE_DESCRIPTOR OutIoDescriptor, _In_ PIO_RESOURCE_DESCRIPTOR IoDescriptor)
Definition: arbiter.h:226
#define TAG_ARBITER
Definition: arbiter.h:10
LONG NTSTATUS
Definition: precomp.h:26
_In_opt_ PWSTR SubkeyName
Definition: cdrom.h:960
Definition: bufpool.h:45
IN PUNICODE_STRING IN POBJECT_ATTRIBUTES ObjectAttributes
Definition: conport.c:36
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
#define L(x)
Definition: resources.c:13
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
NTSTATUS RtlAppendUnicodeToString(IN PUNICODE_STRING Str1, IN PWSTR Str2)
Definition: string_lib.cpp:62
#define PagedPool
Definition: env_spec_w32.h:308
Status
Definition: gdiplustypes.h:24
#define REG_SZ
Definition: layer.c:22
if(dx< 0)
Definition: linetemp.h:194
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
#define InitializeObjectAttributes(p, n, a, r, s)
Definition: reg.c:115
@ KeyValueFullInformationAlign64
Definition: reg.c:111
_Must_inspect_result_ _Out_ PNDIS_STATUS _In_ NDIS_HANDLE _In_ ULONG _Out_ PNDIS_STRING _Out_ PNDIS_HANDLE KeyHandle
Definition: ndis.h:4715
NTSYSAPI NTSTATUS NTAPI ZwClose(_In_ HANDLE Handle)
#define _Inout_
Definition: no_sal2.h:162
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define _In_opt_
Definition: no_sal2.h:212
#define KEY_READ
Definition: nt_native.h:1026
NTSYSAPI VOID NTAPI RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
#define REG_RESOURCE_REQUIREMENTS_LIST
Definition: nt_native.h:1507
#define UNICODE_NULL
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:329
PVOID *typedef PHANDLE
Definition: ntsecpkg.h:455
VOID(NTAPI * PARB_RANGE_CALLBACK)(_In_ PARBITER_INSTANCE Arbiter, _In_opt_ PVOID Context, _In_ ULONGLONG Start, _In_ ULONGLONG End)
Definition: ordering.c:124
static BOOLEAN ArbpDescriptorMatchesArbiter(_In_ PARBITER_INSTANCE Arbiter, _In_ UCHAR DescriptorType)
Definition: ordering.c:209
static NTSTATUS ArbpReadPolicyValue(_In_ HANDLE KeyHandle, _In_ PCWSTR ValueName, _Out_ PKEY_VALUE_FULL_INFORMATION *Value)
Definition: ordering.c:161
static NTSTATUS ArbpOpenPolicySubkey(_In_ PCWSTR SubkeyName, _Out_ PHANDLE KeyHandle)
Definition: ordering.c:134
#define ARBITER_ORDERING_LIMIT
Definition: ordering.c:18
#define ARBP_POLICY_ROOT
Definition: ordering.c:20
static NTSTATUS ArbpCreateOrderingList(_Out_ PARBITER_ORDERING_LIST OrderingList)
Definition: ordering.c:29
#define ARBP_LONGEST_SUBKEY
Definition: ordering.c:21
NTSTATUS NTAPI ArbiterLibDefaultAssignmentOrdering(_Inout_ PARBITER_INSTANCE Arbiter, _In_ PCWSTR AllocationOrderName, _In_ PCWSTR ReservedResourcesName, _In_opt_ PARB_TRANSLATE_ORDERING TranslateOrderingFunction)
Definition: ordering.c:328
static VOID NTAPI ArbpAddOrderingCallback(_In_ PARBITER_INSTANCE Arbiter, _In_opt_ PVOID Context, _In_ ULONGLONG Start, _In_ ULONGLONG End)
Definition: ordering.c:305
static NTSTATUS ArbpForEachRegistryRange(_In_ PARBITER_INSTANCE Arbiter, _In_ PCWSTR Subkey, _In_ PCWSTR ValueName, _In_ PARB_RANGE_CALLBACK Callback, _In_opt_ PVOID Context)
Definition: ordering.c:230
VOID NTAPI ArbiterLibFreeOrderingList(_Inout_ PARBITER_ORDERING_LIST OrderingList)
Definition: ordering.c:52
#define ARBITER_ORDERING_GRANULARITY
Definition: ordering.c:17
NTSTATUS NTAPI ArbiterLibAddOrdering(_Inout_ PARBITER_ORDERING_LIST OrderingList, _In_ UINT64 Start, _In_ UINT64 End)
Definition: ordering.c:70
short WCHAR
Definition: pedump.c:58
static PCWSTR TargetName
Definition: ping.c:67
#define OBJ_KERNEL_HANDLE
Definition: winternl.h:231
#define OBJ_CASE_INSENSITIVE
Definition: winternl.h:228
for(i=0;i< sizeof(testsuite)/sizeof(testsuite[0]);++i) ok(call_test(testsuite[i].func)
#define CmResourceTypeMemory
Definition: restypes.h:106
#define STATUS_SUCCESS
Definition: shellext.h:65
#define STATUS_BUFFER_TOO_SMALL
Definition: shellext.h:69
#define STATUS_BUFFER_OVERFLOW
Definition: shellext.h:66
_In_ PVOID Context
Definition: storport.h:2269
UINT64 Start
Definition: arbiter.h:47
IO_RESOURCE_DESCRIPTOR Descriptors[1]
Definition: iotypes.h:2739
IO_RESOURCE_LIST List[1]
Definition: iotypes.h:2749
uint16_t * PWSTR
Definition: typedefs.h:56
const uint16_t * PCWSTR
Definition: typedefs.h:57
unsigned char UCHAR
Definition: typedefs.h:53
#define NTAPI
Definition: typedefs.h:36
uint64_t ULONGLONG
Definition: typedefs.h:67
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
unsigned char * PUCHAR
Definition: typedefs.h:53
uint32_t ULONG
Definition: typedefs.h:59
#define STATUS_INVALID_PARAMETER
Definition: udferr_usr.h:135
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
_In_ ULONG _In_ PVOID _In_ LONG DescriptorType
Definition: usbdlib.h:160
_In_ WDFCOLLECTION _In_ ULONG Index
_Must_inspect_result_ _In_ WDFDEVICE _In_ DEVICE_REGISTRY_PROPERTY _In_ ULONG _Out_ PULONG ResultLength
Definition: wdfdevice.h:3782
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4539
_In_ WDFINTERRUPT _In_ PFN_WDF_INTERRUPT_SYNCHRONIZE Callback
Definition: wdfinterrupt.h:458
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING ValueName
Definition: wdfregistry.h:243
_Must_inspect_result_ _In_ WDFKEY _In_ PCUNICODE_STRING _Out_opt_ PUSHORT _Inout_opt_ PUNICODE_STRING Value
Definition: wdfregistry.h:413
_In_ WDFIOTARGET Target
Definition: wdfrequest.h:306
_Must_inspect_result_ _In_ WDFIORESLIST _In_ PIO_RESOURCE_DESCRIPTOR Descriptor
Definition: wdfresource.h:342
#define CmResourceTypeMemoryLarge
Definition: cmtypes.h:231
struct _IO_RESOURCE_REQUIREMENTS_LIST * PIO_RESOURCE_REQUIREMENTS_LIST