[HIVEBCD]: Implement an entire wholeseome BCD boot hive, including multiple levels...
[reactos.git] / reactos / boot / environ / lib / misc / bcd.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/misc/bcd.c
5 * PURPOSE: Boot Library BCD Routines
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bl.h"
12 #include <bcd.h>
13
14 /* FUNCTIONS *****************************************************************/
15
16 VOID
17 BiNotifyEnumerationError (
18 _In_ HANDLE ObjectHandle,
19 _In_ PWCHAR ElementName,
20 _In_ NTSTATUS Status
21 )
22 {
23 /* Stub for now */
24 UNREFERENCED_PARAMETER(ObjectHandle);
25 UNREFERENCED_PARAMETER(ElementName);
26 UNREFERENCED_PARAMETER(Status);
27 EfiPrintf(L"Error in BiNotify: %lx for element %s\r\n", Status, ElementName);
28 }
29
30 ULONG
31 BiConvertElementFormatToValueType (
32 _In_ ULONG Format
33 )
34 {
35 /* Strings and objects are strings */
36 if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT))
37 {
38 return REG_SZ;
39 }
40
41 /* Object lists are arrays of strings */
42 if (Format == BCD_TYPE_OBJECT_LIST)
43 {
44 return REG_MULTI_SZ;
45 }
46
47 /* Everything else is binary */
48 return REG_BINARY;
49 }
50
51 NTSTATUS
52 BiConvertRegistryDataToElement (
53 _In_ HANDLE ObjectHandle,
54 _In_ PVOID Data,
55 _In_ ULONG DataLength,
56 _In_ BcdElementType ElementType,
57 _Out_ PVOID Element,
58 _Out_ PULONG ElementSize
59 )
60 {
61 NTSTATUS Status;
62 ULONG Length, Size, ReturnedLength;
63 PBL_DEVICE_DESCRIPTOR Device;
64 BOOLEAN NullTerminate;
65 PBCD_DEVICE_OPTION BcdDevice, ElementDevice;
66 PWCHAR BcdString, ElementString;
67 PGUID ElementGuid; UNICODE_STRING GuidString;
68 PULONGLONG ElementInteger;
69 PUSHORT ElementWord; PBOOLEAN BcdBoolean;
70
71 /* Assume failure */
72 ReturnedLength = 0;
73
74 /* Check what type of format we are dealing with */
75 switch (ElementType.Format)
76 {
77 /* Devices -- they are in a binary format */
78 case BCD_TYPE_DEVICE:
79
80 /* First, make sure it's at least big enough for an empty descriptor */
81 if (DataLength < FIELD_OFFSET(BCD_DEVICE_OPTION,
82 DeviceDescriptor.Unknown))
83 {
84 return STATUS_OBJECT_TYPE_MISMATCH;
85 }
86
87 /* Both the registry and BCD format are the same */
88 BcdDevice = (PBCD_DEVICE_OPTION)Data;
89 ElementDevice = (PBCD_DEVICE_OPTION)Element;
90
91 /* Make sure the device fits in the registry data */
92 Device = &BcdDevice->DeviceDescriptor;
93 Size = Device->Size;
94 if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength)
95 {
96 return STATUS_OBJECT_TYPE_MISMATCH;
97 }
98
99 /* Check if this is a locate device */
100 if (Device->DeviceType == LocateDevice)
101 {
102 EfiPrintf(L"Locates not yet supported\r\n");
103 return STATUS_NOT_SUPPORTED;
104 }
105
106 /* Make sure the caller's buffer can fit the device */
107 ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry);
108 if (ReturnedLength > *ElementSize)
109 {
110 Status = STATUS_BUFFER_TOO_SMALL;
111 break;
112 }
113
114 /* It'll fit -- copy it in */
115 RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size);
116 ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry;
117 Status = STATUS_SUCCESS;
118 break;
119
120 /* Strings -- they are stored as is */
121 case BCD_TYPE_STRING:
122
123 /* Make sure the string isn't empty or misaligned */
124 if (!(DataLength) || (DataLength & 1))
125 {
126 return STATUS_OBJECT_TYPE_MISMATCH;
127 }
128
129 /* Both the registry and BCD format are the same */
130 BcdString = (PWCHAR)Data;
131 ElementString = (PWCHAR)Element;
132
133 /* We'll need as much data as the string has to offer */
134 ReturnedLength = DataLength;
135
136 /* If the string isn't NULL-terminated, do it now */
137 NullTerminate = FALSE;
138 if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL)
139 {
140 ReturnedLength += sizeof(UNICODE_NULL);
141 NullTerminate = TRUE;
142 }
143
144 /* Will we fit in the caller's buffer? */
145 if (ReturnedLength > *ElementSize)
146 {
147 Status = STATUS_BUFFER_TOO_SMALL;
148 break;
149 }
150
151 /* Yep -- copy it in, and NULL-terminate if needed */
152 RtlCopyMemory(Element, Data, DataLength);
153 if (NullTerminate)
154 {
155 ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL;
156 }
157
158 Status = STATUS_SUCCESS;
159 break;
160
161 /* Objects -- they are stored as GUID Strings */
162 case BCD_TYPE_OBJECT:
163
164 /* Registry data is a string, BCD data is a GUID */
165 BcdString = (PWCHAR)Data;
166 ElementGuid = (PGUID)Element;
167
168 /* We need a GUID-sized buffer, does the caller have one? */
169 ReturnedLength = sizeof(*ElementGuid);
170 if (*ElementSize < ReturnedLength)
171 {
172 Status = STATUS_BUFFER_TOO_SMALL;
173 break;
174 }
175
176 /* Yep, copy the GUID */
177 RtlInitUnicodeString(&GuidString, BcdString);
178 Status = RtlGUIDFromString(&GuidString, ElementGuid);
179 break;
180
181 /* Object Lists -- they are stored as arrays of GUID strings */
182 case BCD_TYPE_OBJECT_LIST:
183
184 /* Assume an empty list*/
185 ReturnedLength = 0;
186 Length = 0;
187 Status = STATUS_SUCCESS;
188
189 /* Registry data is an array of strings, BCD data is array of GUIDs */
190 BcdString = (PWCHAR)Data;
191 ElementGuid = (PGUID)Element;
192
193 /* Loop as long as the array still has strings */
194 while (*BcdString)
195 {
196 /* Don't read beyond the registry data */
197 if (Length >= DataLength)
198 {
199 break;
200 }
201
202 /* One more GUID -- does the caller have space? */
203 ReturnedLength += sizeof(GUID);
204 if (ReturnedLength <= *ElementSize)
205 {
206 /* Convert and add it in */
207 RtlInitUnicodeString(&GuidString, BcdString);
208 Status = RtlGUIDFromString(&GuidString, ElementGuid);
209 if (!NT_SUCCESS(Status))
210 {
211 break;
212 }
213
214 /* Move to the next GUID in the caller's buffer */
215 ElementGuid++;
216 }
217
218 /* Move to the next string in the registry array */
219 Size = (wcslen(BcdString) * sizeof(WCHAR)) + sizeof(UNICODE_NULL);
220 Length += Size;
221 BcdString = (PWCHAR)((ULONG_PTR)BcdString + Length);
222 }
223
224 /* Check if we failed anywhere */
225 if (!NT_SUCCESS(Status))
226 {
227 break;
228 }
229
230 /* Check if we consumed more space than we have */
231 if (ReturnedLength > *ElementSize)
232 {
233 Status = STATUS_BUFFER_TOO_SMALL;
234 }
235
236 /* All good here */
237 break;
238
239 /* Integer -- stored as binary */
240 case BCD_TYPE_INTEGER:
241
242 /* BCD data is a ULONGLONG, registry data is 8 bytes binary */
243 ElementInteger = (PULONGLONG)Element;
244 ReturnedLength = sizeof(*ElementInteger);
245
246 /* Make sure the registry data makes sense */
247 if (DataLength > ReturnedLength)
248 {
249 return STATUS_OBJECT_TYPE_MISMATCH;
250 }
251
252 /* Make sure the caller has space */
253 if (*ElementSize < ReturnedLength)
254 {
255 Status = STATUS_BUFFER_TOO_SMALL;
256 break;
257 }
258
259 /* Write the integer result */
260 *ElementInteger = 0;
261 RtlCopyMemory(ElementInteger, Data, DataLength);
262 Status = STATUS_SUCCESS;
263 break;
264
265 /* Boolean -- stored as binary */
266 case BCD_TYPE_BOOLEAN:
267
268 /* BCD data is a BOOLEAN, registry data is 2 bytes binary */
269 ElementWord = (PUSHORT)Element;
270 BcdBoolean = (PBOOLEAN)Data;
271 ReturnedLength = sizeof(ElementWord);
272
273 /* Make sure the registry data makes sense */
274 if (DataLength != sizeof(*BcdBoolean))
275 {
276 return STATUS_OBJECT_TYPE_MISMATCH;
277 }
278
279 /* Make sure the caller has space */
280 if (*ElementSize < ReturnedLength)
281 {
282 Status = STATUS_BUFFER_TOO_SMALL;
283 break;
284 }
285
286 /* Write the boolean result */
287 *ElementWord = 0;
288 *ElementWord = *BcdBoolean != 0;
289 Status = STATUS_SUCCESS;
290 break;
291
292 /* Integer list --stored as binary */
293 case BCD_TYPE_INTEGER_LIST:
294
295 /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */
296 ReturnedLength = DataLength;
297 if (!(DataLength) || (DataLength & 7))
298 {
299 return STATUS_OBJECT_TYPE_MISMATCH;
300 }
301
302 /* Make sure the caller has space */
303 if (*ElementSize < ReturnedLength)
304 {
305 Status = STATUS_BUFFER_TOO_SMALL;
306 break;
307 }
308
309 /* Write the integer list result */
310 RtlCopyMemory(Element, Data, DataLength);
311 Status = STATUS_SUCCESS;
312 break;
313
314 /* Arbitrary data */
315 default:
316
317 /* Registry data is copied binary as-is */
318 ReturnedLength = DataLength;
319
320 /* Make sure it's not empty */
321 if (!DataLength)
322 {
323 return STATUS_OBJECT_TYPE_MISMATCH;
324 }
325
326 /* Make sure the caller has space */
327 if (*ElementSize < ReturnedLength)
328 {
329 Status = STATUS_BUFFER_TOO_SMALL;
330 break;
331 }
332
333 /* Write the result */
334 RtlCopyMemory(Element, Data, DataLength);
335 Status = STATUS_SUCCESS;
336 break;
337 }
338
339 /* If we got here due to success or space issues, write the size */
340 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
341 {
342 *ElementSize = ReturnedLength;
343 }
344
345 /* All done, return our conversion result */
346 return Status;
347 }
348
349 NTSTATUS
350 BiConvertBcdElements (
351 _In_ PBCD_PACKED_ELEMENT Elements,
352 _Out_opt_ PBCD_ELEMENT Buffer,
353 _Inout_ PULONG BufferSize,
354 _Inout_ PULONG ElementCount
355 )
356 {
357 NTSTATUS Status;
358 ULONG ElementSize, AlignedElementSize, AlignedDataSize;
359 PBCD_ELEMENT_HEADER Header;
360 PVOID Data;
361 BOOLEAN Exists;
362 ULONG i, j, Count;
363
364 /* Local variable to keep track of objects */
365 Count = 0;
366
367 /* Safely compute the element bytes needed */
368 Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize);
369 if (!NT_SUCCESS(Status))
370 {
371 return Status;
372 }
373
374 /* Safely align the element size */
375 Status = RtlULongAdd(ElementSize,
376 sizeof(ULONG) - 1,
377 &AlignedElementSize);
378 if (!NT_SUCCESS(Status))
379 {
380 return Status;
381 }
382 AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG);
383
384 /* Do a safe version of Add2Ptr to figure out where the headers will start */
385 Status = RtlULongPtrAdd((ULONG_PTR)Buffer,
386 AlignedElementSize,
387 (PULONG_PTR)&Header);
388 if (!NT_SUCCESS(Status))
389 {
390 return Status;
391 }
392
393 /* Safely compute the header bytes needed */
394 Status = RtlULongMult(*ElementCount,
395 sizeof(BCD_ELEMENT_HEADER),
396 &ElementSize);
397 if (!NT_SUCCESS(Status))
398 {
399 return Status;
400 }
401
402 /* Safely align the header size */
403 Status = RtlULongAdd(ElementSize,
404 AlignedElementSize + sizeof(ULONG) - 1,
405 &AlignedElementSize);
406 if (!NT_SUCCESS(Status))
407 {
408 return Status;
409 }
410 AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG);
411
412 /* Do a safe version of Add2Ptr */
413 Status = RtlULongPtrAdd((ULONG_PTR)Buffer,
414 AlignedElementSize,
415 (PULONG_PTR)&Data);
416 if (!NT_SUCCESS(Status))
417 {
418 return Status;
419 }
420
421 /* Iterate over every element */
422 for (i = 0; i < *ElementCount; i++)
423 {
424 /* Safely align the element size */
425 Status = RtlULongAdd(Elements->Size,
426 sizeof(ULONG) - 1,
427 &AlignedDataSize);
428 if (!NT_SUCCESS(Status))
429 {
430 break;
431 }
432 AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG);
433
434 /* Safely add the size of this data element */
435 Status = RtlULongAdd(AlignedElementSize,
436 AlignedDataSize,
437 &AlignedElementSize);
438 if (!NT_SUCCESS(Status))
439 {
440 break;
441 }
442
443 /* Do we have enough space left? */
444 if (*BufferSize >= AlignedElementSize)
445 {
446 /* Check if our root is an inherited object */
447 Exists = FALSE;
448 if (Elements->RootType.PackedValue == BcdLibraryObjectList_InheritedObjects)
449 {
450 /* Yes, scan for us in the current buffer */
451 for (j = 0; j < Count; j++)
452 {
453 /* Do we already exist? */
454 while (Buffer[j].Header->Type == Elements->RootType.PackedValue)
455 {
456 /* Yep */
457 Exists = TRUE;
458 break;
459 }
460 }
461 }
462
463 /* Have we already found ourselves? */
464 if (!Exists)
465 {
466 /* Nope, one more entry */
467 ++Count;
468
469 /* Write out the unpacked object */
470 Buffer->Body = Data;
471 Buffer->Header = Header;
472
473 /* Fill out its header */
474 Header->Size = Elements->Size;
475 Header->Type = Elements->Type;
476 Header->Version = Elements->Version;
477
478 /* And copy the data */
479 RtlCopyMemory(Data, Elements->Data, Header->Size);
480
481 /* Move to the next unpacked object and header */
482 ++Buffer;
483 ++Header;
484
485 /* Move to the next data entry */
486 Data = (PVOID)((ULONG_PTR)Data + AlignedDataSize);
487 }
488 }
489 else
490 {
491 /* Nope, set failure code, but keep going so we can return count */
492 Status = STATUS_BUFFER_TOO_SMALL;
493 }
494
495 /* Move to the next element entry */
496 Elements = Elements->NextEntry;
497 }
498
499 /* Return the new final buffer size and count */
500 *BufferSize = AlignedElementSize;
501 *ElementCount = Count;
502 return Status;
503 }
504
505 NTSTATUS
506 BcdOpenObject (
507 _In_ HANDLE BcdHandle,
508 _In_ PGUID ObjectId,
509 _Out_ PHANDLE ObjectHandle
510 )
511 {
512 NTSTATUS Status;
513 GUID LocalGuid;
514 UNICODE_STRING GuidString;
515 HANDLE RootObjectHandle;
516
517 /* Assume failure */
518 *ObjectHandle = NULL;
519
520 /* Initialize GUID string */
521 GuidString.Buffer = NULL;
522
523 /* Open the root "Objects" handle */
524 RootObjectHandle = NULL;
525 Status = BiOpenKey(BcdHandle, L"Objects", &RootObjectHandle);
526 if (!NT_SUCCESS(Status))
527 {
528 goto Quickie;
529 }
530
531 /* Capture the object ID and convert it into a string */
532 LocalGuid = *ObjectId;
533 Status = RtlStringFromGUID(&LocalGuid, &GuidString);
534 if (!NT_SUCCESS(Status))
535 {
536 goto Quickie;
537 }
538
539 /* Now open the key containing this object ID */
540 Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle);
541
542 Quickie:
543 /* Free the GUID string if we had one allocated */
544 if (GuidString.Buffer)
545 {
546 RtlFreeUnicodeString(&GuidString);
547 }
548
549 /* Close the root handle if it was open */
550 if (RootObjectHandle)
551 {
552 BiCloseKey(RootObjectHandle);
553 }
554
555 /* Return the final status */
556 return Status;
557 }
558
559 NTSTATUS
560 BcdDeleteElement (
561 _In_ HANDLE ObjectHandle,
562 _In_ ULONG Type
563 )
564 {
565 NTSTATUS Status;
566 HANDLE ElementsHandle, ElementHandle;
567 WCHAR TypeString[22];
568
569 /* Open the elements key */
570 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
571 if (NT_SUCCESS(Status))
572 {
573 /* Convert the element ID into a string */
574 if (!_ultow(Type, TypeString, 16))
575 {
576 /* Failed to do so */
577 Status = STATUS_UNSUCCESSFUL;
578 }
579 else
580 {
581 /* Open the element specifically */
582 Status = BiOpenKey(ElementHandle, TypeString, &ElementHandle);
583 if (NT_SUCCESS(Status))
584 {
585 /* Delete it */
586 Status = BiDeleteKey(ElementHandle);
587 if (NT_SUCCESS(Status))
588 {
589 /* No point in closing the handle anymore */
590 ElementHandle = NULL;
591 }
592 }
593 else
594 {
595 /* The element doesn't exist */
596 Status = STATUS_NOT_FOUND;
597 }
598
599 /* Check if we should close the key */
600 if (ElementHandle)
601 {
602 /* Do it */
603 BiCloseKey(ElementHandle);
604 }
605 }
606 }
607
608 /* Check if we should close the elements handle */
609 if (ElementsHandle)
610 {
611 /* Do it */
612 BiCloseKey(ElementsHandle);
613 }
614
615 /* Return whatever the result was */
616 return Status;
617 }
618
619 NTSTATUS
620 BiEnumerateSubElements (
621 _In_ HANDLE BcdHandle,
622 _In_ PVOID Object,
623 _In_ ULONG ElementType,
624 _In_ ULONG Flags,
625 _Out_opt_ PBCD_PACKED_ELEMENT* Elements,
626 _Inout_ PULONG ElementSize,
627 _Out_ PULONG ElementCount
628 )
629 {
630 NTSTATUS Status;
631 PBCD_PACKED_ELEMENT Element;
632 HANDLE ObjectHandle;
633 ULONG ParsedElements, RequiredSize;
634
635 /* Assume empty */
636 *ElementCount = 0;
637 RequiredSize = 0;
638 ParsedElements = 0;
639
640 /* Open the object */
641 Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle);
642 if (!NT_SUCCESS(Status))
643 {
644 goto Quickie;
645 }
646
647 /* Read the first entry, and the size available */
648 Element = *Elements;
649 RequiredSize = *ElementSize;
650
651 /* Enumerate the object into the element array */
652 Status = BiEnumerateElements(BcdHandle,
653 ObjectHandle,
654 ElementType,
655 Flags,
656 Element,
657 &RequiredSize,
658 &ParsedElements);
659
660 /* Close the handle and bail out if we couldn't enumerate */
661 BiCloseKey(ObjectHandle);
662 if (!NT_SUCCESS(Status))
663 {
664 goto Quickie;
665 }
666
667 /* Check if the and subelements were present */
668 if (ParsedElements)
669 {
670 /* Keep going until the last one */
671 while (Element->NextEntry)
672 {
673 Element = Element->NextEntry;
674 }
675
676 /* Set the new buffer location to the last element */
677 *Elements = Element;
678 }
679
680 Quickie:
681 /* Return the number of sub-elements and their size */
682 *ElementCount = ParsedElements;
683 *ElementSize = RequiredSize;
684 return Status;
685 }
686
687 NTSTATUS
688 BiEnumerateSubObjectElements (
689 _In_ HANDLE BcdHandle,
690 _Out_ PGUID SubObjectList,
691 _In_ ULONG SubObjectCount,
692 _In_ ULONG Flags,
693 _Out_opt_ PBCD_PACKED_ELEMENT Elements,
694 _Inout_ PULONG ElementSize,
695 _Out_ PULONG ElementCount
696 )
697 {
698 NTSTATUS Status;
699 ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i;
700 PBCD_PACKED_ELEMENT PreviousElement;
701
702 /* Assume empty list */
703 *ElementCount = 0;
704 Status = STATUS_SUCCESS;
705
706 /* Initialize variables */
707 TotalSize = 0;
708 PreviousElement = NULL;
709
710 /* Set the currently remaining size based on caller's input */
711 CurrentSize = *ElementSize;
712
713 /* Iterate over every subje object */
714 for (i = 0; i < SubObjectCount; i++)
715 {
716 /* Set the currently remaining buffer space */
717 RequiredSize = CurrentSize;
718
719 /* Enumerate the inherited sub elements */
720 Status = BiEnumerateSubElements(BcdHandle,
721 &SubObjectList[i],
722 BcdLibraryObjectList_InheritedObjects,
723 Flags,
724 &Elements,
725 &RequiredSize,
726 &SubElementCount);
727 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
728 {
729 /* Safely add the length of the sub elements */
730 Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize);
731 if (!NT_SUCCESS(Status))
732 {
733 break;
734 }
735
736 /* Add the sub elements to the total */
737 *ElementCount += SubElementCount;
738
739 /* See if we have enough space*/
740 if (*ElementSize >= TotalSize)
741 {
742 /* Were there any subelements? */
743 if (SubElementCount)
744 {
745 /* Update to keep track of these new subelements */
746 CurrentSize = *ElementSize - TotalSize;
747
748 /* Link the subelements into the chain */
749 PreviousElement = Elements;
750 PreviousElement->NextEntry =
751 (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize);
752 Elements = PreviousElement->NextEntry;
753 }
754 }
755 else
756 {
757 /* We're out of space */
758 CurrentSize = 0;
759 }
760 }
761 else if ((Status != STATUS_NOT_FOUND) &&
762 (Status != STATUS_OBJECT_NAME_NOT_FOUND))
763 {
764 /* Some other fatal error, break out */
765 break;
766 }
767 else
768 {
769 /* The sub element was not found, print a warning but keep going */
770 BlStatusPrint(L"Ignoring missing BCD inherit object: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
771 (&SubObjectList[i])->Data1,
772 (&SubObjectList[i])->Data2,
773 (&SubObjectList[i])->Data3,
774 (&SubObjectList[i])->Data4[0],
775 (&SubObjectList[i])->Data4[1],
776 (&SubObjectList[i])->Data4[2],
777 (&SubObjectList[i])->Data4[3],
778 (&SubObjectList[i])->Data4[4],
779 (&SubObjectList[i])->Data4[5],
780 (&SubObjectList[i])->Data4[6],
781 (&SubObjectList[i])->Data4[7],
782 (&SubObjectList[i])->Data4[8]);
783 Status = STATUS_SUCCESS;
784 }
785 }
786
787 /* Terminate the last element, if one was left */
788 if (PreviousElement)
789 {
790 PreviousElement->NextEntry = NULL;
791 }
792
793 /* Set failure code if we ran out of space */
794 if (*ElementSize < TotalSize)
795 {
796 Status = STATUS_BUFFER_TOO_SMALL;
797 }
798
799 /* Return final length and status */
800 *ElementSize = TotalSize;
801 return Status;
802 }
803
804 NTSTATUS
805 BiEnumerateElements (
806 _In_ HANDLE BcdHandle,
807 _In_ HANDLE ObjectHandle,
808 _In_ ULONG RootElementType,
809 _In_ ULONG Flags,
810 _Out_opt_ PBCD_PACKED_ELEMENT Elements,
811 _Inout_ PULONG ElementSize,
812 _Out_ PULONG ElementCount
813 )
814 {
815 HANDLE ElementsHandle, ElementHandle;
816 ULONG TotalLength, RegistryElementDataLength, RemainingLength;
817 NTSTATUS Status;
818 ULONG i;
819 PVOID ElementData, SubObjectList, RegistryElementData;
820 BcdElementType ElementType;
821 PBCD_PACKED_ELEMENT PreviousElement, ElementsStart;
822 ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength;
823 PWCHAR ElementName;
824 PWCHAR* SubKeys;
825
826 /* Assume failure */
827 *ElementCount = 0;
828
829 /* Initialize all locals that are checked at the end*/
830 SubKeys = NULL;
831 ElementsHandle = NULL;
832 ElementHandle = NULL;
833 ElementData = NULL;
834 RegistryElementData = NULL;
835 PreviousElement = NULL;
836 ElementName = NULL;
837 SubObjectList = NULL;
838 TotalLength = 0;
839 ElementDataLength = 0;
840 SubObjectCount = 0;
841 RemainingLength = 0;
842 ElementsStart = Elements;
843
844 /* Open the root object key's elements */
845 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
846 if (!NT_SUCCESS(Status))
847 {
848 goto Quickie;
849 }
850
851 /* Enumerate all elements */
852 Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount);
853 if (!NT_SUCCESS(Status))
854 {
855 goto Quickie;
856 }
857
858 /* Iterate over each one */
859 for (i = 0; i < SubKeyCount; i++)
860 {
861 /* Open the element */
862 ElementName = SubKeys[i];
863 Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle);
864 if (!NT_SUCCESS(Status))
865 {
866 EfiPrintf(L"ELEMENT ERROR: %lx\r\n", Status);
867 EfiStall(100000);
868 break;
869 }
870
871 /* The name of the element is its data type */
872 ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16);
873 if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1))
874 {
875 EfiPrintf(L"Value invalid\r\n");
876 BiCloseKey(ElementHandle);
877 ElementHandle = 0;
878 continue;
879 }
880
881 /* Read the appropriate registry value type for this element */
882 Status = BiGetRegistryValue(ElementHandle,
883 L"Element",
884 NULL,
885 BiConvertElementFormatToValueType(
886 ElementType.Format),
887 &RegistryElementData,
888 &RegistryElementDataLength);
889 if (!NT_SUCCESS(Status))
890 {
891 EfiPrintf(L"Element invalid\r\n");
892 break;
893 }
894
895 /* Now figure out how much space the converted element will need */
896 ElementDataLength = 0;
897 Status = BiConvertRegistryDataToElement(ObjectHandle,
898 RegistryElementData,
899 RegistryElementDataLength,
900 ElementType,
901 NULL,
902 &ElementDataLength);
903 if (Status != STATUS_BUFFER_TOO_SMALL)
904 {
905 break;
906 }
907
908 /* Allocate a buffer big enough for the converted element */
909 ElementData = BlMmAllocateHeap(ElementDataLength);
910 if (!ElementData)
911 {
912 Status = STATUS_INSUFFICIENT_RESOURCES;
913 break;
914 }
915
916 /* And actually convert it this time around */
917 Status = BiConvertRegistryDataToElement(ObjectHandle,
918 RegistryElementData,
919 RegistryElementDataLength,
920 ElementType,
921 ElementData,
922 &ElementDataLength);
923 if (!NT_SUCCESS(Status))
924 {
925 break;
926 }
927
928 /* Safely add space for the packed element header */
929 Status = RtlULongAdd(TotalLength,
930 FIELD_OFFSET(BCD_PACKED_ELEMENT, Data),
931 &TotalLength);
932 if (!NT_SUCCESS(Status))
933 {
934 break;
935 }
936
937 /* Safely add space for the data of the element itself */
938 Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength);
939 if (!NT_SUCCESS(Status))
940 {
941 break;
942 }
943
944 /* One more element */
945 ++*ElementCount;
946
947 /* See how much space we were given */
948 RemainingLength = *ElementSize;
949 if (RemainingLength >= TotalLength)
950 {
951 /* Set the next pointer */
952 Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + TotalLength);
953
954 /* Fill this one out */
955 Elements->RootType.PackedValue = RootElementType;
956 Elements->Version = 1;
957 Elements->Type = ElementType.PackedValue;
958 Elements->Size = ElementDataLength;
959
960 /* Add the data */
961 RtlCopyMemory(Elements->Data, ElementData, ElementDataLength);
962 RemainingLength -= TotalLength;
963
964 /* Move to the next element on the next pass */
965 PreviousElement = Elements;
966 Elements = Elements->NextEntry;
967 }
968 else
969 {
970 /* We're out of space */
971 RemainingLength = 0;
972 }
973
974 /* Are we enumerating devices, and is this a device? */
975 if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) &&
976 (ElementType.Format == BCD_TYPE_DEVICE))
977 {
978 /* Yep, so go inside to enumerate it */
979 Status = BiEnumerateSubElements(BcdHandle,
980 ElementData,
981 ElementType.PackedValue,
982 Flags,
983 &Elements,
984 &ElementDataLength,
985 &SubElementCount);
986 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
987 {
988 /* Safely add the length of the sub elements */
989 Status = RtlULongAdd(TotalLength,
990 ElementDataLength,
991 &TotalLength);
992 if (!NT_SUCCESS(Status))
993 {
994 break;
995 }
996
997 /* Add the sub elements to the total */
998 *ElementCount += SubElementCount;
999
1000 /* See if we have enough space*/
1001 if (*ElementSize >= TotalLength)
1002 {
1003 /* Were there any subelements? */
1004 if (SubElementCount)
1005 {
1006 /* Update to keep track of these new subelements */
1007 ElementDataLength = *ElementSize - TotalLength;
1008
1009 /* Link the subelements into the chain */
1010 PreviousElement = Elements;
1011 PreviousElement->NextEntry =
1012 (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart +
1013 TotalLength);
1014 Elements = PreviousElement->NextEntry;
1015 }
1016 }
1017 else
1018 {
1019 /* We're out of space */
1020 ElementDataLength = 0;
1021 }
1022 }
1023 else if ((Status != STATUS_NOT_FOUND) &&
1024 (Status != STATUS_OBJECT_NAME_NOT_FOUND))
1025 {
1026 /* Fatal error trying to read the data, so fail */
1027 break;
1028 }
1029 }
1030 else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) &&
1031 (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects))
1032 {
1033 /* Inherited objects are requsted, so allocate a buffer for them */
1034 SubObjectList = BlMmAllocateHeap(ElementDataLength);
1035 if (!SubObjectList)
1036 {
1037 Status = STATUS_INSUFFICIENT_RESOURCES;
1038 break;
1039 }
1040
1041 /* Copy the elements into the list. They are arrays of GUIDs */
1042 RtlCopyMemory(SubObjectList, ElementData, ElementDataLength);
1043 SubObjectCount = ElementDataLength / sizeof(GUID);
1044 }
1045
1046 /* Free our local buffers */
1047 BlMmFreeHeap(ElementData);
1048 BlMmFreeHeap(RegistryElementData);
1049 ElementData = NULL;
1050 RegistryElementData = NULL;
1051
1052 /* Close the key */
1053 BiCloseKey(ElementHandle);
1054 ElementHandle = NULL;
1055 ElementName = NULL;
1056 }
1057
1058 /* Did we end up here with a sub object list after successful loop parsing? */
1059 if ((i != 0) && (i == SubKeyCount) && (SubObjectList))
1060 {
1061 /* We will actually enumerate it now, at the end */
1062 Status = BiEnumerateSubObjectElements(BcdHandle,
1063 SubObjectList,
1064 SubObjectCount,
1065 Flags,
1066 Elements,
1067 &RemainingLength,
1068 &SubElementCount);
1069 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
1070 {
1071 /* Safely add the length of the sub elements */
1072 Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength);
1073 if ((NT_SUCCESS(Status)) && (SubElementCount))
1074 {
1075 /* Add the sub elements to the total */
1076 *ElementCount += SubElementCount;
1077
1078 /* Don't touch PreviousElement anymore */
1079 PreviousElement = NULL;
1080 }
1081 }
1082 }
1083
1084 Quickie:
1085 /* Free the sub object list, if any */
1086 if (SubObjectList)
1087 {
1088 BlMmFreeHeap(SubObjectList);
1089 }
1090
1091 /* Free any local element data */
1092 if (ElementData)
1093 {
1094 BlMmFreeHeap(ElementData);
1095 }
1096
1097 /* Free any local registry data */
1098 if (RegistryElementData)
1099 {
1100 BlMmFreeHeap(RegistryElementData);
1101 }
1102
1103 /* Close the handle if still opened */
1104 if (ElementHandle)
1105 {
1106 BiCloseKey(ElementHandle);
1107 }
1108
1109 /* Terminate the last element, if any */
1110 if (PreviousElement)
1111 {
1112 PreviousElement->NextEntry = NULL;
1113 }
1114
1115 /* Close the root handle if still opened */
1116 if (ElementsHandle)
1117 {
1118 BiCloseKey(ElementsHandle);
1119 }
1120
1121 /* Set failure code if out of space */
1122 if (*ElementSize < TotalLength)
1123 {
1124 Status = STATUS_BUFFER_TOO_SMALL;
1125 }
1126
1127 /* Other errors will send a notification error */
1128 if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL))
1129 {
1130 BiNotifyEnumerationError(ObjectHandle, ElementName, Status);
1131 }
1132
1133 /* Finally free the subkeys array */
1134 if (SubKeys)
1135 {
1136 BlMmFreeHeap(SubKeys);
1137 }
1138
1139 /* And return the required, final length and status */
1140 *ElementSize = TotalLength;
1141 return Status;
1142 }
1143
1144 NTSTATUS
1145 BiAddStoreFromFile (
1146 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
1147 _Out_ PHANDLE StoreHandle
1148 )
1149 {
1150 NTSTATUS Status;
1151 HANDLE HiveHandle, KeyHandle;
1152
1153 /* Load the specified hive */
1154 Status = BiLoadHive(FilePath, &HiveHandle);
1155 if (!NT_SUCCESS(Status))
1156 {
1157 return Status;
1158 }
1159
1160 /* Open the description key to make sure this is really a BCD */
1161 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
1162 if (NT_SUCCESS(Status))
1163 {
1164 /* It is -- close the key as we don't need it */
1165 BiCloseKey(KeyHandle);
1166 *StoreHandle = HiveHandle;
1167 }
1168 else
1169 {
1170 /* Failure, drop a reference on the hive and close the key */
1171 BiDereferenceHive(HiveHandle);
1172 BiCloseKey(HiveHandle);
1173 }
1174
1175 /* Return the status */
1176 return Status;
1177 }
1178
1179 NTSTATUS
1180 BcdEnumerateAndUnpackElements (
1181 _In_ HANDLE BcdHandle,
1182 _In_ HANDLE ObjectHandle,
1183 _Out_opt_ PBCD_ELEMENT Elements,
1184 _Inout_ PULONG ElementSize,
1185 _Out_ PULONG ElementCount
1186 )
1187 {
1188 PVOID LocalElements;
1189 NTSTATUS Status;
1190 ULONG LocalElementCount, LocalElementSize;
1191
1192 /* Make sure required parameters are there */
1193 if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize)))
1194 {
1195 return STATUS_INVALID_PARAMETER;
1196 }
1197
1198 /* Set initial count to zero */
1199 *ElementCount = 0;
1200
1201 /* Do the initial enumeration to figure out the size required */
1202 LocalElementSize = 0;
1203 LocalElementCount = 0;
1204 Status = BiEnumerateElements(BcdHandle,
1205 ObjectHandle,
1206 0,
1207 BCD_ENUMERATE_FLAG_IN_ORDER |
1208 BCD_ENUMERATE_FLAG_DEVICES |
1209 BCD_ENUMERATE_FLAG_DEEP,
1210 NULL,
1211 &LocalElementSize,
1212 &LocalElementCount);
1213 if (Status != STATUS_BUFFER_TOO_SMALL)
1214 {
1215 return Status;
1216 }
1217
1218 /* Now allocate a buffer large enough to hold them */
1219 LocalElements = BlMmAllocateHeap(LocalElementSize);
1220 if (!LocalElements)
1221 {
1222 return STATUS_INSUFFICIENT_RESOURCES;
1223 }
1224
1225 /* Zero out the array and do the real enumeration this time around */
1226 RtlZeroMemory(LocalElements, LocalElementSize);
1227 Status = BiEnumerateElements(BcdHandle,
1228 ObjectHandle,
1229 0,
1230 BCD_ENUMERATE_FLAG_IN_ORDER |
1231 BCD_ENUMERATE_FLAG_DEVICES |
1232 BCD_ENUMERATE_FLAG_DEEP,
1233 LocalElements,
1234 &LocalElementSize,
1235 &LocalElementCount);
1236 if (!NT_SUCCESS(Status))
1237 {
1238 return Status;
1239 }
1240
1241 /* Now we know the real count */
1242 *ElementCount = LocalElementCount;
1243
1244 /* Now unpack the data */
1245 Status = BiConvertBcdElements(LocalElements,
1246 Elements,
1247 ElementSize,
1248 &LocalElementCount);
1249 if (NT_SUCCESS(Status))
1250 {
1251 /* Not all elements may have been converted */
1252 *ElementCount = LocalElementCount;
1253 }
1254
1255 /* Free the local (unpacked) buffer and return status */
1256 BlMmFreeHeap(LocalElements);
1257 return Status;
1258 }
1259
1260 NTSTATUS
1261 BcdOpenStoreFromFile (
1262 _In_ PUNICODE_STRING FileName,
1263 _In_ PHANDLE BcdHandle
1264 )
1265 {
1266 ULONG Length;
1267 PBL_FILE_PATH_DESCRIPTOR FilePath;
1268 NTSTATUS Status;
1269 HANDLE LocalHandle;
1270
1271 /* Assume failure */
1272 LocalHandle = NULL;
1273
1274 /* Allocate a path descriptor */
1275 Length = FileName->Length + sizeof(*FilePath);
1276 FilePath = BlMmAllocateHeap(Length);
1277 if (!FilePath)
1278 {
1279 return STATUS_NO_MEMORY;
1280 }
1281
1282 /* Initialize it */
1283 FilePath->Version = 1;
1284 FilePath->PathType = InternalPath;
1285 FilePath->Length = Length;
1286
1287 /* Copy the name and NULL-terminate it */
1288 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
1289 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
1290
1291 /* Open the BCD */
1292 Status = BiAddStoreFromFile(FilePath, &LocalHandle);
1293 if (NT_SUCCESS(Status))
1294 {
1295 /* Return the handle on success */
1296 *BcdHandle = LocalHandle;
1297 }
1298
1299 /* Free the descriptor and return the status */
1300 BlMmFreeHeap(FilePath);
1301 return Status;
1302 }
1303