1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * PURPOSE: Configuration Manager - Boot Initialization
5 * COPYRIGHT: Copyright 2007 Alex Ionescu (alex.ionescu@reactos.org)
6 * Copyright 2010 ReactOS Portable Systems Group
7 * Copyright 2022 Hermès Bélusca-Maïto
8 *
9 * NOTE: This module is shared by both the kernel and the bootloader.
10 */
11
12 /* INCLUDES *******************************************************************/
13
14 #include <ntoskrnl.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #ifdef _BLDR_
20
21 #undef CODE_SEG
22 #define CODE_SEG(...)
23
24 #include <ntstrsafe.h>
25 #include <cmlib.h>
26 #include "internal/cmboot.h"
27
28 // HACK: This is part of non-NT-compatible SafeBoot support in kernel.
29 ULONG InitSafeBootMode = 0;
30
31 DBG_DEFAULT_CHANNEL(REGISTRY);
32 #define CMTRACE(x, fmt, ...) TRACE(fmt, ##__VA_ARGS__) // DPRINT
33
34 #endif /* _BLDR_ */
35
36
37 /* DEFINES ********************************************************************/
38
39 #define CM_BOOT_DEBUG 0x20
40
41 #define IS_NULL_TERMINATED(Buffer, Size) \
42 (((Size) >= sizeof(WCHAR)) && ((Buffer)[(Size) / sizeof(WCHAR) - 1] == UNICODE_NULL))
43
44
45 /* FUNCTIONS ******************************************************************/
46
47 // HACK: This is part of non-NT-compatible SafeBoot support in kernel.
48 extern ULONG InitSafeBootMode;
49
50 CODE_SEG("INIT")
51 static
52 BOOLEAN
53 CmpIsSafe(
54 _In_ PHHIVE Hive,
55 _In_ HCELL_INDEX SafeBootCell,
56 _In_ HCELL_INDEX DriverCell);
57
58 /**
59 * @brief
60 * Finds the corresponding "HKLM\SYSTEM\ControlSetXXX" system control set
61 * registry key, according to the "Current", "Default", or "LastKnownGood"
62 * values in the "HKLM\SYSTEM\Select" registry key.
63 *
64 * @param[in] SystemHive
65 * The SYSTEM hive.
66 *
67 * @param[in] RootCell
68 * The root cell of the SYSTEM hive.
69 *
70 * @param[in] SelectKeyName
71 * The control set to check for: either "Current", "Default", or
72 * "LastKnownGood", the value of which selects the corresponding
73 * "HKLM\SYSTEM\ControlSetXXX" control set registry key.
74 *
75 * @param[out] AutoSelect
76 * Value of the "AutoSelect" registry value (unused).
77 *
78 * @return
79 * The control set registry key's hive cell (if found), or HCELL_NIL.
80 **/
81 CODE_SEG("INIT")
82 HCELL_INDEX
83 NTAPI
CmpFindControlSet(_In_ PHHIVE SystemHive,_In_ HCELL_INDEX RootCell,_In_ PCUNICODE_STRING SelectKeyName,_Out_ PBOOLEAN AutoSelect)84 CmpFindControlSet(
85 _In_ PHHIVE SystemHive,
86 _In_ HCELL_INDEX RootCell,
87 _In_ PCUNICODE_STRING SelectKeyName,
88 _Out_ PBOOLEAN AutoSelect)
89 {
90 UNICODE_STRING Name;
91 PCM_KEY_NODE Node;
92 HCELL_INDEX SelectCell, AutoSelectCell, SelectValueCell, ControlSetCell;
93 HCELL_INDEX CurrentValueCell;
94 PCM_KEY_VALUE Value;
95 ULONG Length;
96 NTSTATUS Status;
97 PULONG CurrentData;
98 PULONG ControlSetId;
99 WCHAR Buffer[128];
100
101 /* Sanity check: We shouldn't need to release any acquired cells */
102 ASSERT(SystemHive->ReleaseCellRoutine == NULL);
103
104 /* Get the Select key */
105 RtlInitUnicodeString(&Name, L"select");
106 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, RootCell);
107 if (!Node) return HCELL_NIL;
108 SelectCell = CmpFindSubKeyByName(SystemHive, Node, &Name);
109 if (SelectCell == HCELL_NIL) return HCELL_NIL;
110
111 /* Get AutoSelect value */
112 RtlInitUnicodeString(&Name, L"AutoSelect");
113 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell);
114 if (!Node) return HCELL_NIL;
115 AutoSelectCell = CmpFindValueByName(SystemHive, Node, &Name);
116 if (AutoSelectCell == HCELL_NIL)
117 {
118 /* Assume TRUE if the value is missing */
119 *AutoSelect = TRUE;
120 }
121 else
122 {
123 /* Read the value */
124 Value = (PCM_KEY_VALUE)HvGetCell(SystemHive, AutoSelectCell);
125 if (!Value) return HCELL_NIL;
126 // if (Value->Type != REG_DWORD) return HCELL_NIL;
127
128 /* Convert it to a boolean */
129 CurrentData = (PULONG)CmpValueToData(SystemHive, Value, &Length);
130 if (!CurrentData) return HCELL_NIL;
131 // if (Length < sizeof(ULONG)) return HCELL_NIL;
132
133 *AutoSelect = *(PBOOLEAN)CurrentData;
134 }
135
136 /* Now find the control set being looked up */
137 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell);
138 if (!Node) return HCELL_NIL;
139 SelectValueCell = CmpFindValueByName(SystemHive, Node, SelectKeyName);
140 if (SelectValueCell == HCELL_NIL) return HCELL_NIL;
141
142 /* Read the value (corresponding to the CCS ID) */
143 Value = (PCM_KEY_VALUE)HvGetCell(SystemHive, SelectValueCell);
144 if (!Value) return HCELL_NIL;
145 if (Value->Type != REG_DWORD) return HCELL_NIL;
146 ControlSetId = (PULONG)CmpValueToData(SystemHive, Value, &Length);
147 if (!ControlSetId) return HCELL_NIL;
148 if (Length < sizeof(ULONG)) return HCELL_NIL;
149
150 /* Now build the CCS's Name */
151 Status = RtlStringCbPrintfW(Buffer, sizeof(Buffer),
152 L"ControlSet%03lu", *ControlSetId);
153 if (!NT_SUCCESS(Status)) return HCELL_NIL;
154 /* RtlStringCbPrintfW ensures the buffer to be NULL-terminated */
155 RtlInitUnicodeString(&Name, Buffer);
156
157 /* Now open it */
158 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, RootCell);
159 if (!Node) return HCELL_NIL;
160 ControlSetCell = CmpFindSubKeyByName(SystemHive, Node, &Name);
161 if (ControlSetCell == HCELL_NIL) return HCELL_NIL;
162
163 /* Get the value of the "Current" CCS */
164 RtlInitUnicodeString(&Name, L"Current");
165 Node = (PCM_KEY_NODE)HvGetCell(SystemHive, SelectCell);
166 if (!Node) return HCELL_NIL;
167 CurrentValueCell = CmpFindValueByName(SystemHive, Node, &Name);
168
169 /* Make sure it exists */
170 if (CurrentValueCell != HCELL_NIL)
171 {
172 /* Get the current value and make sure it's a ULONG */
173 Value = (PCM_KEY_VALUE)HvGetCell(SystemHive, CurrentValueCell);
174 if (!Value) return HCELL_NIL;
175 if (Value->Type == REG_DWORD)
176 {
177 /* Get the data and update it */
178 CurrentData = (PULONG)CmpValueToData(SystemHive, Value, &Length);
179 if (!CurrentData) return HCELL_NIL;
180 if (Length < sizeof(ULONG)) return HCELL_NIL;
181
182 *CurrentData = *ControlSetId;
183 }
184 }
185
186 /* Return the CCS cell */
187 return ControlSetCell;
188 }
189
190 /**
191 * @brief
192 * Finds the index of the driver's "Tag" value
193 * in its corresponding group ordering list.
194 *
195 * @param[in] Hive
196 * The SYSTEM hive.
197 *
198 * @param[in] TagCell
199 * The driver's "Tag" registry value's hive cell.
200 *
201 * @param[in] GroupOrderCell
202 * The hive cell of the "Control\GroupOrderList" registry key
203 * inside the currently selected control set.
204 *
205 * @param[in] GroupName
206 * The driver's group name.
207 *
208 * @return
209 * The corresponding tag index, or -1 (last position),
210 * or -2 (next-to-last position).
211 **/
212 CODE_SEG("INIT")
213 static
214 ULONG
CmpFindTagIndex(_In_ PHHIVE Hive,_In_ HCELL_INDEX TagCell,_In_ HCELL_INDEX GroupOrderCell,_In_ PCUNICODE_STRING GroupName)215 CmpFindTagIndex(
216 _In_ PHHIVE Hive,
217 _In_ HCELL_INDEX TagCell,
218 _In_ HCELL_INDEX GroupOrderCell,
219 _In_ PCUNICODE_STRING GroupName)
220 {
221 PCM_KEY_VALUE TagValue, Value;
222 PCM_KEY_NODE Node;
223 HCELL_INDEX OrderCell;
224 PULONG DriverTag, TagOrder;
225 ULONG CurrentTag, Length;
226 BOOLEAN BufferAllocated;
227
228 /* Sanity check: We shouldn't need to release any acquired cells */
229 ASSERT(Hive->ReleaseCellRoutine == NULL);
230
231 /* Get the tag */
232 Value = (PCM_KEY_VALUE)HvGetCell(Hive, TagCell);
233 if (!Value) return -2;
234 if (Value->Type != REG_DWORD) return -2;
235 DriverTag = (PULONG)CmpValueToData(Hive, Value, &Length);
236 if (!DriverTag) return -2;
237 if (Length < sizeof(ULONG)) return -2;
238
239 /* Get the order array */
240 Node = (PCM_KEY_NODE)HvGetCell(Hive, GroupOrderCell);
241 if (!Node) return -2;
242 OrderCell = CmpFindValueByName(Hive, Node, GroupName);
243 if (OrderCell == HCELL_NIL) return -2;
244
245 /* And read it */
246 TagValue = (PCM_KEY_VALUE)HvGetCell(Hive, OrderCell);
247 if (!TagValue) return -2;
248 if (!CmpGetValueData(Hive,
249 TagValue,
250 &Length,
251 (PVOID*)&TagOrder,
252 &BufferAllocated,
253 &OrderCell)
254 || !TagOrder)
255 {
256 return -2;
257 }
258
259 /* Parse each tag */
260 for (CurrentTag = 1; CurrentTag <= TagOrder[0]; CurrentTag++)
261 {
262 /* Find a match */
263 if (TagOrder[CurrentTag] == *DriverTag)
264 {
265 /* Found it -- return the tag */
266 if (BufferAllocated) Hive->Free(TagOrder, Length);
267 return CurrentTag;
268 }
269 }
270
271 /* No matches, so assume next to last ordering */
272 if (BufferAllocated) Hive->Free(TagOrder, Length);
273 return -2;
274 }
275
276 #ifdef _BLDR_
277
278 /**
279 * @brief
280 * Checks whether the specified named driver is already in the driver list.
281 * Optionally returns its corresponding driver node.
282 *
283 * @remarks Used in bootloader only.
284 *
285 * @param[in] DriverListHead
286 * The driver list.
287 *
288 * @param[in] DriverName
289 * The name of the driver to search for.
290 *
291 * @param[out] FoundDriver
292 * Optional pointer that receives in output the address of the
293 * matching driver node, if any, or NULL if none has been found.
294 *
295 * @return
296 * TRUE if the driver has been found, FALSE if not.
297 **/
298 CODE_SEG("INIT")
299 BOOLEAN
300 NTAPI
CmpIsDriverInList(_In_ PLIST_ENTRY DriverListHead,_In_ PCUNICODE_STRING DriverName,_Out_opt_ PBOOT_DRIVER_NODE * FoundDriver)301 CmpIsDriverInList(
302 _In_ PLIST_ENTRY DriverListHead,
303 _In_ PCUNICODE_STRING DriverName,
304 _Out_opt_ PBOOT_DRIVER_NODE* FoundDriver)
305 {
306 PLIST_ENTRY Entry;
307 PBOOT_DRIVER_NODE DriverNode;
308
309 for (Entry = DriverListHead->Flink;
310 Entry != DriverListHead;
311 Entry = Entry->Flink)
312 {
313 DriverNode = CONTAINING_RECORD(Entry,
314 BOOT_DRIVER_NODE,
315 ListEntry.Link);
316
317 if (RtlEqualUnicodeString(&DriverNode->Name,
318 DriverName,
319 TRUE))
320 {
321 /* The driver node has been found */
322 if (FoundDriver)
323 *FoundDriver = DriverNode;
324 return TRUE;
325 }
326 }
327
328 /* None has been found */
329 if (FoundDriver)
330 *FoundDriver = NULL;
331 return FALSE;
332 }
333
334 #endif /* _BLDR_ */
335
336 /**
337 * @brief
338 * Inserts the specified driver entry into the driver list.
339 *
340 * @param[in] Hive
341 * The SYSTEM hive.
342 *
343 * @param[in] DriverCell
344 * The registry key's hive cell of the driver to be added, inside
345 * the "Services" sub-key of the currently selected control set.
346 *
347 * @param[in] GroupOrderCell
348 * The hive cell of the "Control\GroupOrderList" registry key
349 * inside the currently selected control set.
350 *
351 * @param[in] RegistryPath
352 * Constant UNICODE_STRING pointing to
353 * "\\Registry\\Machine\\System\\CurrentControlSet\\Services\\".
354 *
355 * @param[in,out] DriverListHead
356 * The driver list where to insert the driver entry.
357 *
358 * @return
359 * TRUE if the driver has been inserted into the list, FALSE if not.
360 **/
361 CODE_SEG("INIT")
362 BOOLEAN
363 NTAPI
CmpAddDriverToList(_In_ PHHIVE Hive,_In_ HCELL_INDEX DriverCell,_In_ HCELL_INDEX GroupOrderCell,_In_ PCUNICODE_STRING RegistryPath,_Inout_ PLIST_ENTRY DriverListHead)364 CmpAddDriverToList(
365 _In_ PHHIVE Hive,
366 _In_ HCELL_INDEX DriverCell,
367 _In_ HCELL_INDEX GroupOrderCell,
368 _In_ PCUNICODE_STRING RegistryPath,
369 _Inout_ PLIST_ENTRY DriverListHead)
370 {
371 PBOOT_DRIVER_NODE DriverNode;
372 PBOOT_DRIVER_LIST_ENTRY DriverEntry;
373 PCM_KEY_NODE Node;
374 PCM_KEY_VALUE Value;
375 ULONG Length;
376 USHORT NameLength;
377 HCELL_INDEX ValueCell, TagCell;
378 PUNICODE_STRING FilePath, RegistryString;
379 UNICODE_STRING Name;
380 PULONG ErrorControl;
381 PWCHAR Buffer;
382
383 /* Sanity check: We shouldn't need to release any acquired cells */
384 ASSERT(Hive->ReleaseCellRoutine == NULL);
385
386 /* Allocate a driver node and initialize it */
387 DriverNode = Hive->Allocate(sizeof(BOOT_DRIVER_NODE), FALSE, TAG_CM);
388 if (!DriverNode)
389 return FALSE;
390
391 RtlZeroMemory(DriverNode, sizeof(BOOT_DRIVER_NODE));
392 DriverEntry = &DriverNode->ListEntry;
393
394 /* Get the driver cell */
395 Node = (PCM_KEY_NODE)HvGetCell(Hive, DriverCell);
396 if (!Node)
397 goto Failure;
398
399 /* Get the name from the cell */
400 NameLength = (Node->Flags & KEY_COMP_NAME) ?
401 CmpCompressedNameSize(Node->Name, Node->NameLength) :
402 Node->NameLength;
403 if (NameLength < sizeof(WCHAR))
404 goto Failure;
405
406 /* Now allocate the buffer for it and copy the name */
407 RtlInitEmptyUnicodeString(&DriverNode->Name,
408 Hive->Allocate(NameLength, FALSE, TAG_CM),
409 NameLength);
410 if (!DriverNode->Name.Buffer)
411 goto Failure;
412
413 DriverNode->Name.Length = NameLength;
414 if (Node->Flags & KEY_COMP_NAME)
415 {
416 /* Compressed name */
417 CmpCopyCompressedName(DriverNode->Name.Buffer,
418 DriverNode->Name.Length,
419 Node->Name,
420 Node->NameLength);
421 }
422 else
423 {
424 /* Normal name */
425 RtlCopyMemory(DriverNode->Name.Buffer, Node->Name, Node->NameLength);
426 }
427
428 /* Now find the image path */
429 RtlInitUnicodeString(&Name, L"ImagePath");
430 ValueCell = CmpFindValueByName(Hive, Node, &Name);
431 if (ValueCell == HCELL_NIL)
432 {
433 /* Could not find it, so assume the drivers path */
434 Length = sizeof(L"System32\\Drivers\\") + NameLength + sizeof(L".sys");
435
436 /* Allocate the path name */
437 FilePath = &DriverEntry->FilePath;
438 RtlInitEmptyUnicodeString(FilePath,
439 Hive->Allocate(Length, FALSE, TAG_CM),
440 (USHORT)Length);
441 if (!FilePath->Buffer)
442 goto Failure;
443
444 /* Write the path name */
445 if (!NT_SUCCESS(RtlAppendUnicodeToString(FilePath, L"System32\\Drivers\\")) ||
446 !NT_SUCCESS(RtlAppendUnicodeStringToString(FilePath, &DriverNode->Name)) ||
447 !NT_SUCCESS(RtlAppendUnicodeToString(FilePath, L".sys")))
448 {
449 goto Failure;
450 }
451 }
452 else
453 {
454 /* Path name exists, so grab it */
455 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell);
456 if (!Value)
457 goto Failure;
458 if ((Value->Type != REG_SZ) && (Value->Type != REG_EXPAND_SZ))
459 goto Failure;
460 Buffer = (PWCHAR)CmpValueToData(Hive, Value, &Length);
461 if (!Buffer)
462 goto Failure;
463 if (IS_NULL_TERMINATED(Buffer, Length))
464 Length -= sizeof(UNICODE_NULL);
465 if (Length < sizeof(WCHAR))
466 goto Failure;
467
468 /* Allocate and setup the path name */
469 FilePath = &DriverEntry->FilePath;
470 RtlInitEmptyUnicodeString(FilePath,
471 Hive->Allocate(Length, FALSE, TAG_CM),
472 (USHORT)Length);
473 if (!FilePath->Buffer)
474 goto Failure;
475
476 /* Transfer the data */
477 RtlCopyMemory(FilePath->Buffer, Buffer, Length);
478 FilePath->Length = (USHORT)Length;
479 }
480
481 /* Now build the registry path */
482 RegistryString = &DriverEntry->RegistryPath;
483 Length = RegistryPath->Length + NameLength;
484 RtlInitEmptyUnicodeString(RegistryString,
485 Hive->Allocate(Length, FALSE, TAG_CM),
486 (USHORT)Length);
487 if (!RegistryString->Buffer)
488 goto Failure;
489
490 /* Add the driver name to it */
491 if (!NT_SUCCESS(RtlAppendUnicodeStringToString(RegistryString, RegistryPath)) ||
492 !NT_SUCCESS(RtlAppendUnicodeStringToString(RegistryString, &DriverNode->Name)))
493 {
494 goto Failure;
495 }
496
497 /* The entry is done, add it */
498 InsertHeadList(DriverListHead, &DriverEntry->Link);
499
500 /* Now find error control settings */
501 RtlInitUnicodeString(&Name, L"ErrorControl");
502 ValueCell = CmpFindValueByName(Hive, Node, &Name);
503 if (ValueCell == HCELL_NIL)
504 {
505 /* Could not find it, so assume default */
506 DriverNode->ErrorControl = NormalError;
507 }
508 else
509 {
510 /* Otherwise, read whatever the data says */
511 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell);
512 if (!Value)
513 goto Failure;
514 if (Value->Type != REG_DWORD)
515 goto Failure;
516 ErrorControl = (PULONG)CmpValueToData(Hive, Value, &Length);
517 if (!ErrorControl)
518 goto Failure;
519 if (Length < sizeof(ULONG))
520 goto Failure;
521
522 DriverNode->ErrorControl = *ErrorControl;
523 }
524
525 /* Next, get the group cell */
526 RtlInitUnicodeString(&Name, L"group");
527 ValueCell = CmpFindValueByName(Hive, Node, &Name);
528 if (ValueCell == HCELL_NIL)
529 {
530 /* Could not find it, so set an empty string */
531 RtlInitEmptyUnicodeString(&DriverNode->Group, NULL, 0);
532 }
533 else
534 {
535 /* Found it, read the group value */
536 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell);
537 if (!Value)
538 goto Failure;
539 if (Value->Type != REG_SZ) // REG_EXPAND_SZ not really allowed there.
540 goto Failure;
541
542 /* Copy it into the node */
543 Buffer = (PWCHAR)CmpValueToData(Hive, Value, &Length);
544 if (!Buffer)
545 goto Failure;
546 if (IS_NULL_TERMINATED(Buffer, Length))
547 Length -= sizeof(UNICODE_NULL);
548
549 DriverNode->Group.Buffer = Buffer;
550 DriverNode->Group.Length = (USHORT)Length;
551 DriverNode->Group.MaximumLength = DriverNode->Group.Length;
552 }
553
554 /* Finally, find the tag */
555 RtlInitUnicodeString(&Name, L"Tag");
556 TagCell = CmpFindValueByName(Hive, Node, &Name);
557 if (TagCell == HCELL_NIL)
558 {
559 /* No tag, so load last */
560 DriverNode->Tag = -1;
561 }
562 else
563 {
564 /* Otherwise, decode it based on tag order */
565 DriverNode->Tag = CmpFindTagIndex(Hive,
566 TagCell,
567 GroupOrderCell,
568 &DriverNode->Group);
569 }
570
571 CMTRACE(CM_BOOT_DEBUG, "Adding boot driver: '%wZ', '%wZ'\n",
572 &DriverNode->Name, &DriverEntry->FilePath);
573
574 /* All done! */
575 return TRUE;
576
577 Failure:
578 if (DriverEntry->RegistryPath.Buffer)
579 {
580 Hive->Free(DriverEntry->RegistryPath.Buffer,
581 DriverEntry->RegistryPath.MaximumLength);
582 }
583 if (DriverEntry->FilePath.Buffer)
584 {
585 Hive->Free(DriverEntry->FilePath.Buffer,
586 DriverEntry->FilePath.MaximumLength);
587 }
588 if (DriverNode->Name.Buffer)
589 {
590 Hive->Free(DriverNode->Name.Buffer,
591 DriverNode->Name.MaximumLength);
592 }
593 Hive->Free(DriverNode, sizeof(BOOT_DRIVER_NODE));
594
595 return FALSE;
596 }
597
598 /**
599 * @brief
600 * Checks whether the specified driver has the expected load type.
601 *
602 * @param[in] Hive
603 * The SYSTEM hive.
604 *
605 * @param[in] DriverCell
606 * The registry key's hive cell of the driver, inside the
607 * "Services" sub-key of the currently selected control set.
608 *
609 * @param[in] LoadType
610 * The load type the driver should match.
611 *
612 * @return
613 * TRUE if the driver's load type matches, FALSE if not.
614 **/
615 CODE_SEG("INIT")
616 static
617 BOOLEAN
CmpIsLoadType(_In_ PHHIVE Hive,_In_ HCELL_INDEX Cell,_In_ SERVICE_LOAD_TYPE LoadType)618 CmpIsLoadType(
619 _In_ PHHIVE Hive,
620 _In_ HCELL_INDEX Cell,
621 _In_ SERVICE_LOAD_TYPE LoadType)
622 {
623 PCM_KEY_NODE Node;
624 PCM_KEY_VALUE Value;
625 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"Start");
626 HCELL_INDEX ValueCell;
627 ULONG Length;
628 PULONG Data;
629
630 /* Sanity check: We shouldn't need to release any acquired cells */
631 ASSERT(Hive->ReleaseCellRoutine == NULL);
632
633 /* Open the start cell */
634 Node = (PCM_KEY_NODE)HvGetCell(Hive, Cell);
635 if (!Node) return FALSE;
636 ValueCell = CmpFindValueByName(Hive, Node, &Name);
637 if (ValueCell == HCELL_NIL) return FALSE;
638
639 /* Read the start value */
640 Value = (PCM_KEY_VALUE)HvGetCell(Hive, ValueCell);
641 if (!Value) return FALSE;
642 if (Value->Type != REG_DWORD) return FALSE;
643 Data = (PULONG)CmpValueToData(Hive, Value, &Length);
644 if (!Data) return FALSE;
645 if (Length < sizeof(ULONG)) return FALSE;
646
647 /* Return if the type matches */
648 return (*Data == LoadType);
649 }
650
651 /**
652 * @brief
653 * Enumerates all drivers within the given control set and load type,
654 * present in the "Services" sub-key, and inserts them into the driver list.
655 *
656 * @param[in] Hive
657 * The SYSTEM hive.
658 *
659 * @param[in] ControlSet
660 * The control set registry key's hive cell.
661 *
662 * @param[in] LoadType
663 * The load type the driver should match.
664 *
665 * @param[in] BootFileSystem
666 * Optional name of the boot file system, for which to insert
667 * its corresponding driver.
668 *
669 * @param[in,out] DriverListHead
670 * The driver list where to insert the enumerated drivers.
671 *
672 * @return
673 * TRUE if the drivers have been successfully enumerated and inserted,
674 * FALSE if not.
675 **/
676 CODE_SEG("INIT")
677 BOOLEAN
678 NTAPI
CmpFindDrivers(_In_ PHHIVE Hive,_In_ HCELL_INDEX ControlSet,_In_ SERVICE_LOAD_TYPE LoadType,_In_opt_ PCWSTR BootFileSystem,_Inout_ PLIST_ENTRY DriverListHead)679 CmpFindDrivers(
680 _In_ PHHIVE Hive,
681 _In_ HCELL_INDEX ControlSet,
682 _In_ SERVICE_LOAD_TYPE LoadType,
683 _In_opt_ PCWSTR BootFileSystem,
684 _Inout_ PLIST_ENTRY DriverListHead)
685 {
686 HCELL_INDEX ServicesCell, ControlCell, GroupOrderCell, DriverCell;
687 HCELL_INDEX SafeBootCell = HCELL_NIL;
688 ULONG i;
689 UNICODE_STRING Name;
690 UNICODE_STRING KeyPath;
691 PCM_KEY_NODE ControlNode, ServicesNode, Node;
692 PBOOT_DRIVER_NODE FsNode;
693
694 /* Sanity check: We shouldn't need to release any acquired cells */
695 ASSERT(Hive->ReleaseCellRoutine == NULL);
696
697 /* Open the control set key */
698 ControlNode = (PCM_KEY_NODE)HvGetCell(Hive, ControlSet);
699 if (!ControlNode) return FALSE;
700
701 /* Get services cell */
702 RtlInitUnicodeString(&Name, L"Services");
703 ServicesCell = CmpFindSubKeyByName(Hive, ControlNode, &Name);
704 if (ServicesCell == HCELL_NIL) return FALSE;
705
706 /* Open services key */
707 ServicesNode = (PCM_KEY_NODE)HvGetCell(Hive, ServicesCell);
708 if (!ServicesNode) return FALSE;
709
710 /* Get control cell */
711 RtlInitUnicodeString(&Name, L"Control");
712 ControlCell = CmpFindSubKeyByName(Hive, ControlNode, &Name);
713 if (ControlCell == HCELL_NIL) return FALSE;
714
715 /* Get the group order cell and read it */
716 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlCell);
717 if (!Node) return FALSE;
718 RtlInitUnicodeString(&Name, L"GroupOrderList");
719 GroupOrderCell = CmpFindSubKeyByName(Hive, Node, &Name);
720 if (GroupOrderCell == HCELL_NIL) return FALSE;
721
722 /* Get Safe Boot cell */
723 if (InitSafeBootMode)
724 {
725 /* Open the Safe Boot key */
726 RtlInitUnicodeString(&Name, L"SafeBoot");
727 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlCell);
728 if (!Node) return FALSE;
729 SafeBootCell = CmpFindSubKeyByName(Hive, Node, &Name);
730 if (SafeBootCell == HCELL_NIL) return FALSE;
731
732 /* Open the correct start key (depending on the mode) */
733 Node = (PCM_KEY_NODE)HvGetCell(Hive, SafeBootCell);
734 if (!Node) return FALSE;
735 switch (InitSafeBootMode)
736 {
737 /* NOTE: Assumes MINIMAL (1) and DSREPAIR (3) load same items */
738 case 1:
739 case 3: RtlInitUnicodeString(&Name, L"Minimal"); break;
740 case 2: RtlInitUnicodeString(&Name, L"Network"); break;
741 default: return FALSE;
742 }
743 SafeBootCell = CmpFindSubKeyByName(Hive, Node, &Name);
744 if (SafeBootCell == HCELL_NIL) return FALSE;
745 }
746
747 /* Build the root registry path */
748 RtlInitUnicodeString(&KeyPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
749
750 /* Enumerate each sub-key */
751 i = 0;
752 DriverCell = CmpFindSubKeyByNumber(Hive, ServicesNode, i);
753 while (DriverCell != HCELL_NIL)
754 {
755 /* Make sure it's a driver of this start type AND is "safe" to load */
756 if (CmpIsLoadType(Hive, DriverCell, LoadType) &&
757 CmpIsSafe(Hive, SafeBootCell, DriverCell))
758 {
759 /* Add it to the list */
760 if (!CmpAddDriverToList(Hive,
761 DriverCell,
762 GroupOrderCell,
763 &KeyPath,
764 DriverListHead))
765 {
766 CMTRACE(CM_BOOT_DEBUG, " Failed to add boot driver\n");
767 }
768 }
769
770 /* Go to the next sub-key */
771 DriverCell = CmpFindSubKeyByNumber(Hive, ServicesNode, ++i);
772 }
773
774 /* Check if we have a boot file system */
775 if (BootFileSystem)
776 {
777 /* Find it */
778 RtlInitUnicodeString(&Name, BootFileSystem);
779 DriverCell = CmpFindSubKeyByName(Hive, ServicesNode, &Name);
780 if (DriverCell != HCELL_NIL)
781 {
782 CMTRACE(CM_BOOT_DEBUG, "Adding Boot FileSystem '%S'\n",
783 BootFileSystem);
784
785 /* Always add it to the list */
786 if (!CmpAddDriverToList(Hive,
787 DriverCell,
788 GroupOrderCell,
789 &KeyPath,
790 DriverListHead))
791 {
792 CMTRACE(CM_BOOT_DEBUG, " Failed to add boot driver\n");
793 }
794 else
795 {
796 /* Mark it as critical so it always loads */
797 FsNode = CONTAINING_RECORD(DriverListHead->Flink,
798 BOOT_DRIVER_NODE,
799 ListEntry.Link);
800 FsNode->ErrorControl = SERVICE_ERROR_CRITICAL;
801 }
802 }
803 }
804
805 /* We're done! */
806 return TRUE;
807 }
808
809 /**
810 * @brief
811 * Performs the driver list sorting, according to the ordering list.
812 *
813 * @param[in] Hive
814 * The SYSTEM hive.
815 *
816 * @param[in] ControlSet
817 * The control set registry key's hive cell.
818 *
819 * @param[in,out] DriverListHead
820 * The driver list to sort.
821 *
822 * @return
823 * TRUE if sorting has been successfully done, FALSE if not.
824 **/
825 CODE_SEG("INIT")
826 static
827 BOOLEAN
CmpDoSort(_Inout_ PLIST_ENTRY DriverListHead,_In_ PCUNICODE_STRING OrderList)828 CmpDoSort(
829 _Inout_ PLIST_ENTRY DriverListHead,
830 _In_ PCUNICODE_STRING OrderList)
831 {
832 PWCHAR Current, End = NULL;
833 UNICODE_STRING GroupName;
834 PLIST_ENTRY NextEntry;
835 PBOOT_DRIVER_NODE CurrentNode;
836
837 /* We're going from end to start, so get to the last group and keep going */
838 Current = &OrderList->Buffer[OrderList->Length / sizeof(WCHAR)];
839 while (Current > OrderList->Buffer)
840 {
841 /* Scan the current string */
842 do
843 {
844 if (*Current == UNICODE_NULL) End = Current;
845 } while ((*(--Current - 1) != UNICODE_NULL) && (Current != OrderList->Buffer));
846
847 /* This is our cleaned up string for this specific group */
848 ASSERT(End != NULL);
849 GroupName.Length = (USHORT)(End - Current) * sizeof(WCHAR);
850 GroupName.MaximumLength = GroupName.Length;
851 GroupName.Buffer = Current;
852
853 /* Now loop the driver list */
854 NextEntry = DriverListHead->Flink;
855 while (NextEntry != DriverListHead)
856 {
857 /* Get this node */
858 CurrentNode = CONTAINING_RECORD(NextEntry,
859 BOOT_DRIVER_NODE,
860 ListEntry.Link);
861
862 /* Get the next entry now since we'll do a relink */
863 NextEntry = NextEntry->Flink;
864
865 /* Is there a group name and does it match the current group? */
866 if (CurrentNode->Group.Buffer &&
867 RtlEqualUnicodeString(&GroupName, &CurrentNode->Group, TRUE))
868 {
869 /* Remove from this location and re-link in the new one */
870 RemoveEntryList(&CurrentNode->ListEntry.Link);
871 InsertHeadList(DriverListHead, &CurrentNode->ListEntry.Link);
872 }
873 }
874
875 /* Move on */
876 --Current;
877 }
878
879 /* All done */
880 return TRUE;
881 }
882
883 /**
884 * @brief
885 * Sorts the driver list, according to the drivers' group load ordering.
886 *
887 * @param[in] Hive
888 * The SYSTEM hive.
889 *
890 * @param[in] ControlSet
891 * The control set registry key's hive cell.
892 *
893 * @param[in,out] DriverListHead
894 * The driver list to sort.
895 *
896 * @return
897 * TRUE if sorting has been successfully done, FALSE if not.
898 **/
899 CODE_SEG("INIT")
900 BOOLEAN
901 NTAPI
CmpSortDriverList(_In_ PHHIVE Hive,_In_ HCELL_INDEX ControlSet,_Inout_ PLIST_ENTRY DriverListHead)902 CmpSortDriverList(
903 _In_ PHHIVE Hive,
904 _In_ HCELL_INDEX ControlSet,
905 _Inout_ PLIST_ENTRY DriverListHead)
906 {
907 PCM_KEY_NODE Node;
908 PCM_KEY_VALUE ListValue;
909 HCELL_INDEX ControlCell, GroupOrder, ListCell;
910 UNICODE_STRING Name, OrderList;
911 ULONG Length;
912
913 /* Sanity check: We shouldn't need to release any acquired cells */
914 ASSERT(Hive->ReleaseCellRoutine == NULL);
915
916 /* Open the control key */
917 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlSet);
918 if (!Node) return FALSE;
919 RtlInitUnicodeString(&Name, L"Control");
920 ControlCell = CmpFindSubKeyByName(Hive, Node, &Name);
921 if (ControlCell == HCELL_NIL) return FALSE;
922
923 /* Open the service group order */
924 Node = (PCM_KEY_NODE)HvGetCell(Hive, ControlCell);
925 if (!Node) return FALSE;
926 RtlInitUnicodeString(&Name, L"ServiceGroupOrder");
927 GroupOrder = CmpFindSubKeyByName(Hive, Node, &Name);
928 if (GroupOrder == HCELL_NIL) return FALSE;
929
930 /* Open the list key */
931 Node = (PCM_KEY_NODE)HvGetCell(Hive, GroupOrder);
932 if (!Node) return FALSE;
933 RtlInitUnicodeString(&Name, L"list");
934 ListCell = CmpFindValueByName(Hive, Node, &Name);
935 if (ListCell == HCELL_NIL) return FALSE;
936
937 /* Read the actual list */
938 ListValue = (PCM_KEY_VALUE)HvGetCell(Hive, ListCell);
939 if (!ListValue) return FALSE;
940 if (ListValue->Type != REG_MULTI_SZ) return FALSE;
941
942 /* Copy it into a buffer */
943 OrderList.Buffer = (PWCHAR)CmpValueToData(Hive, ListValue, &Length);
944 if (!OrderList.Buffer) return FALSE;
945 if (!IS_NULL_TERMINATED(OrderList.Buffer, Length)) return FALSE;
946 OrderList.Length = (USHORT)Length - sizeof(UNICODE_NULL);
947 OrderList.MaximumLength = OrderList.Length;
948
949 /* And start the sort algorithm */
950 return CmpDoSort(DriverListHead, &OrderList);
951 }
952
953 CODE_SEG("INIT")
954 static
955 BOOLEAN
CmpOrderGroup(_In_ PBOOT_DRIVER_NODE StartNode,_In_ PBOOT_DRIVER_NODE EndNode)956 CmpOrderGroup(
957 _In_ PBOOT_DRIVER_NODE StartNode,
958 _In_ PBOOT_DRIVER_NODE EndNode)
959 {
960 PBOOT_DRIVER_NODE CurrentNode, PreviousNode;
961 PLIST_ENTRY ListEntry;
962
963 /* Base case, nothing to do */
964 if (StartNode == EndNode) return TRUE;
965
966 /* Loop the nodes */
967 CurrentNode = StartNode;
968 do
969 {
970 /* Save this as the previous node */
971 PreviousNode = CurrentNode;
972
973 /* And move to the next one */
974 ListEntry = CurrentNode->ListEntry.Link.Flink;
975 CurrentNode = CONTAINING_RECORD(ListEntry,
976 BOOT_DRIVER_NODE,
977 ListEntry.Link);
978
979 /* Check if the previous driver had a bigger tag */
980 if (PreviousNode->Tag > CurrentNode->Tag)
981 {
982 /* Check if we need to update the tail */
983 if (CurrentNode == EndNode)
984 {
985 /* Update the tail */
986 ListEntry = CurrentNode->ListEntry.Link.Blink;
987 EndNode = CONTAINING_RECORD(ListEntry,
988 BOOT_DRIVER_NODE,
989 ListEntry.Link);
990 }
991
992 /* Remove this driver since we need to move it */
993 RemoveEntryList(&CurrentNode->ListEntry.Link);
994
995 /* Keep looping until we find a driver with a lower tag than ours */
996 while ((PreviousNode->Tag > CurrentNode->Tag) && (PreviousNode != StartNode))
997 {
998 /* We'll be re-inserted at this spot */
999 ListEntry = PreviousNode->ListEntry.Link.Blink;
1000 PreviousNode = CONTAINING_RECORD(ListEntry,
1001 BOOT_DRIVER_NODE,
1002 ListEntry.Link);
1003 }
1004
1005 /* Do the insert in the new location */
1006 InsertTailList(&PreviousNode->ListEntry.Link, &CurrentNode->ListEntry.Link);
1007
1008 /* Update the head, if needed */
1009 if (PreviousNode == StartNode) StartNode = CurrentNode;
1010 }
1011 } while (CurrentNode != EndNode);
1012
1013 /* All done */
1014 return TRUE;
1015 }
1016
1017 /**
1018 * @brief
1019 * Removes potential circular dependencies (cycles) and sorts the driver list.
1020 *
1021 * @param[in,out] DriverListHead
1022 * The driver list to sort.
1023 *
1024 * @return
1025 * Always TRUE.
1026 **/
1027 CODE_SEG("INIT")
1028 BOOLEAN
1029 NTAPI
CmpResolveDriverDependencies(_Inout_ PLIST_ENTRY DriverListHead)1030 CmpResolveDriverDependencies(
1031 _Inout_ PLIST_ENTRY DriverListHead)
1032 {
1033 PLIST_ENTRY NextEntry;
1034 PBOOT_DRIVER_NODE StartNode, EndNode, CurrentNode;
1035
1036 /* Loop the list */
1037 NextEntry = DriverListHead->Flink;
1038 while (NextEntry != DriverListHead)
1039 {
1040 /* Find the first entry */
1041 StartNode = CONTAINING_RECORD(NextEntry,
1042 BOOT_DRIVER_NODE,
1043 ListEntry.Link);
1044 do
1045 {
1046 /* Find the last entry */
1047 EndNode = CONTAINING_RECORD(NextEntry,
1048 BOOT_DRIVER_NODE,
1049 ListEntry.Link);
1050
1051 /* Get the next entry */
1052 NextEntry = NextEntry->Flink;
1053 CurrentNode = CONTAINING_RECORD(NextEntry,
1054 BOOT_DRIVER_NODE,
1055 ListEntry.Link);
1056
1057 /* If the next entry is back to the top, break out */
1058 if (NextEntry == DriverListHead) break;
1059
1060 /* Otherwise, check if this entry is equal */
1061 if (!RtlEqualUnicodeString(&StartNode->Group,
1062 &CurrentNode->Group,
1063 TRUE))
1064 {
1065 /* It is, so we've detected a cycle, break out */
1066 break;
1067 }
1068 } while (NextEntry != DriverListHead);
1069
1070 /* Now we have the correct start and end pointers, so do the sort */
1071 CmpOrderGroup(StartNode, EndNode);
1072 }
1073
1074 /* We're done */
1075 return TRUE;
1076 }
1077
1078 CODE_SEG("INIT")
1079 static
1080 BOOLEAN
CmpIsSafe(_In_ PHHIVE Hive,_In_ HCELL_INDEX SafeBootCell,_In_ HCELL_INDEX DriverCell)1081 CmpIsSafe(
1082 _In_ PHHIVE Hive,
1083 _In_ HCELL_INDEX SafeBootCell,
1084 _In_ HCELL_INDEX DriverCell)
1085 {
1086 PCM_KEY_NODE SafeBootNode;
1087 PCM_KEY_NODE DriverNode;
1088 PCM_KEY_VALUE KeyValue;
1089 HCELL_INDEX CellIndex;
1090 ULONG Length;
1091 UNICODE_STRING Name;
1092 PWCHAR Buffer;
1093
1094 /* Sanity check: We shouldn't need to release any acquired cells */
1095 ASSERT(Hive->ReleaseCellRoutine == NULL);
1096
1097 /* Driver key node (mandatory) */
1098 ASSERT(DriverCell != HCELL_NIL);
1099 DriverNode = (PCM_KEY_NODE)HvGetCell(Hive, DriverCell);
1100 if (!DriverNode) return FALSE;
1101
1102 /* Safe boot key node (optional but return TRUE if not present) */
1103 if (SafeBootCell == HCELL_NIL) return TRUE;
1104 SafeBootNode = (PCM_KEY_NODE)HvGetCell(Hive, SafeBootCell);
1105 if (!SafeBootNode) return FALSE;
1106
1107 /* Search by the name from the group */
1108 RtlInitUnicodeString(&Name, L"Group");
1109 CellIndex = CmpFindValueByName(Hive, DriverNode, &Name);
1110 if (CellIndex != HCELL_NIL)
1111 {
1112 KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
1113 if (!KeyValue) return FALSE;
1114
1115 if (KeyValue->Type == REG_SZ) // REG_EXPAND_SZ not really allowed there.
1116 {
1117 /* Compose the search 'key' */
1118 Buffer = (PWCHAR)CmpValueToData(Hive, KeyValue, &Length);
1119 if (!Buffer)
1120 return FALSE;
1121 if (IS_NULL_TERMINATED(Buffer, Length))
1122 Length -= sizeof(UNICODE_NULL);
1123
1124 Name.Buffer = Buffer;
1125 Name.Length = (USHORT)Length;
1126 Name.MaximumLength = Name.Length;
1127
1128 /* Search for corresponding key in the Safe Boot key */
1129 CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name);
1130 if (CellIndex != HCELL_NIL) return TRUE;
1131 }
1132 }
1133
1134 /* Group has not been found - find driver name */
1135 Length = (DriverNode->Flags & KEY_COMP_NAME) ?
1136 CmpCompressedNameSize(DriverNode->Name, DriverNode->NameLength) :
1137 DriverNode->NameLength;
1138 if (Length < sizeof(WCHAR))
1139 return FALSE;
1140
1141 /* Now allocate the buffer for it and copy the name */
1142 RtlInitEmptyUnicodeString(&Name,
1143 Hive->Allocate(Length, FALSE, TAG_CM),
1144 (USHORT)Length);
1145 if (!Name.Buffer)
1146 return FALSE;
1147
1148 Name.Length = (USHORT)Length;
1149 if (DriverNode->Flags & KEY_COMP_NAME)
1150 {
1151 /* Compressed name */
1152 CmpCopyCompressedName(Name.Buffer,
1153 Name.Length,
1154 DriverNode->Name,
1155 DriverNode->NameLength);
1156 }
1157 else
1158 {
1159 /* Normal name */
1160 RtlCopyMemory(Name.Buffer, DriverNode->Name, DriverNode->NameLength);
1161 }
1162
1163 CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name);
1164 Hive->Free(Name.Buffer, Name.MaximumLength);
1165 if (CellIndex != HCELL_NIL) return TRUE;
1166
1167 /* Not group or driver name - search by image name */
1168 RtlInitUnicodeString(&Name, L"ImagePath");
1169 CellIndex = CmpFindValueByName(Hive, DriverNode, &Name);
1170 if (CellIndex != HCELL_NIL)
1171 {
1172 KeyValue = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
1173 if (!KeyValue) return FALSE;
1174
1175 if ((KeyValue->Type == REG_SZ) || (KeyValue->Type == REG_EXPAND_SZ))
1176 {
1177 /* Compose the search 'key' */
1178 Buffer = (PWCHAR)CmpValueToData(Hive, KeyValue, &Length);
1179 if (!Buffer) return FALSE;
1180 if (Length < sizeof(WCHAR)) return FALSE;
1181
1182 /* Get the base image file name */
1183 // FIXME: wcsrchr() may fail if Buffer is *not* NULL-terminated!
1184 Name.Buffer = wcsrchr(Buffer, OBJ_NAME_PATH_SEPARATOR);
1185 if (!Name.Buffer) return FALSE;
1186 ++Name.Buffer;
1187
1188 /* Length of the base name must be >=1 WCHAR */
1189 if (((ULONG_PTR)Name.Buffer - (ULONG_PTR)Buffer) >= Length)
1190 return FALSE;
1191 Length -= ((ULONG_PTR)Name.Buffer - (ULONG_PTR)Buffer);
1192 if (IS_NULL_TERMINATED(Name.Buffer, Length))
1193 Length -= sizeof(UNICODE_NULL);
1194 if (Length < sizeof(WCHAR)) return FALSE;
1195
1196 Name.Length = (USHORT)Length;
1197 Name.MaximumLength = Name.Length;
1198
1199 /* Search for corresponding key in the Safe Boot key */
1200 CellIndex = CmpFindSubKeyByName(Hive, SafeBootNode, &Name);
1201 if (CellIndex != HCELL_NIL) return TRUE;
1202 }
1203 }
1204
1205 /* Nothing found - nothing else to search */
1206 return FALSE;
1207 }
1208
1209 /**
1210 * @brief
1211 * Empties the driver list and frees all allocated driver nodes in it.
1212 *
1213 * @param[in] Hive
1214 * The SYSTEM hive (used only for the Hive->Free() memory deallocator).
1215 *
1216 * @param[in,out] DriverListHead
1217 * The driver list to free.
1218 *
1219 * @return None
1220 **/
1221 CODE_SEG("INIT")
1222 VOID
1223 NTAPI
CmpFreeDriverList(_In_ PHHIVE Hive,_Inout_ PLIST_ENTRY DriverListHead)1224 CmpFreeDriverList(
1225 _In_ PHHIVE Hive,
1226 _Inout_ PLIST_ENTRY DriverListHead)
1227 {
1228 PLIST_ENTRY Entry;
1229 PBOOT_DRIVER_NODE DriverNode;
1230
1231 /* Loop through the list and remove each driver node */
1232 while (!IsListEmpty(DriverListHead))
1233 {
1234 /* Get the driver node */
1235 Entry = RemoveHeadList(DriverListHead);
1236 DriverNode = CONTAINING_RECORD(Entry,
1237 BOOT_DRIVER_NODE,
1238 ListEntry.Link);
1239
1240 /* Free any allocated string buffers, then the node */
1241 if (DriverNode->ListEntry.RegistryPath.Buffer)
1242 {
1243 Hive->Free(DriverNode->ListEntry.RegistryPath.Buffer,
1244 DriverNode->ListEntry.RegistryPath.MaximumLength);
1245 }
1246 if (DriverNode->ListEntry.FilePath.Buffer)
1247 {
1248 Hive->Free(DriverNode->ListEntry.FilePath.Buffer,
1249 DriverNode->ListEntry.FilePath.MaximumLength);
1250 }
1251 if (DriverNode->Name.Buffer)
1252 {
1253 Hive->Free(DriverNode->Name.Buffer,
1254 DriverNode->Name.MaximumLength);
1255 }
1256 Hive->Free(DriverNode, sizeof(BOOT_DRIVER_NODE));
1257 }
1258 }
1259
1260 /* EOF */
1261