xref: /reactos/boot/environ/lib/misc/bcd.c (revision c2c66aff)
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(ElementsHandle, 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             Status = STATUS_SUCCESS;
783         }
784     }
785 
786     /* Terminate the last element, if one was left */
787     if (PreviousElement)
788     {
789         PreviousElement->NextEntry = NULL;
790     }
791 
792     /* Set failure code if we ran out of space */
793     if (*ElementSize < TotalSize)
794     {
795         Status = STATUS_BUFFER_TOO_SMALL;
796     }
797 
798     /* Return final length and status */
799     *ElementSize = TotalSize;
800     return Status;
801 }
802 
803 NTSTATUS
804 BiEnumerateElements (
805     _In_ HANDLE BcdHandle,
806     _In_ HANDLE ObjectHandle,
807     _In_ ULONG RootElementType,
808     _In_ ULONG Flags,
809     _Out_opt_ PBCD_PACKED_ELEMENT Elements,
810     _Inout_ PULONG ElementSize,
811     _Out_ PULONG ElementCount
812     )
813 {
814     HANDLE ElementsHandle, ElementHandle;
815     ULONG TotalLength, RegistryElementDataLength, RemainingLength;
816     NTSTATUS Status;
817     ULONG i;
818     PVOID ElementData, SubObjectList, RegistryElementData;
819     BcdElementType ElementType;
820     PBCD_PACKED_ELEMENT PreviousElement, ElementsStart;
821     ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength;
822     PWCHAR ElementName;
823     PWCHAR* SubKeys;
824 
825     /* Assume failure */
826     *ElementCount = 0;
827 
828     /* Initialize all locals that are checked at the end*/
829     SubKeys = NULL;
830     ElementsHandle = NULL;
831     ElementHandle = NULL;
832     ElementData = NULL;
833     RegistryElementData = NULL;
834     PreviousElement = NULL;
835     ElementName = NULL;
836     SubObjectList = NULL;
837     TotalLength = 0;
838     ElementDataLength = 0;
839     SubObjectCount = 0;
840     RemainingLength = 0;
841     ElementsStart = Elements;
842 
843     /* Open the root object key's elements */
844     Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
845     if (!NT_SUCCESS(Status))
846     {
847         goto Quickie;
848     }
849 
850     /* Enumerate all elements */
851     Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount);
852     if (!NT_SUCCESS(Status))
853     {
854         goto Quickie;
855     }
856 
857     /* Iterate over each one */
858     for (i = 0; i < SubKeyCount; i++)
859     {
860         /* Open the element */
861         ElementName = SubKeys[i];
862         Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle);
863         if (!NT_SUCCESS(Status))
864         {
865             EfiPrintf(L"ELEMENT ERROR: %lx\r\n", Status);
866             EfiStall(100000);
867             break;
868         }
869 
870         /* The name of the element is its data type */
871         ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16);
872         if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1))
873         {
874             EfiPrintf(L"Value invalid\r\n");
875             BiCloseKey(ElementHandle);
876             ElementHandle = NULL;
877             continue;
878         }
879 
880         /* Read the appropriate registry value type for this element */
881         Status = BiGetRegistryValue(ElementHandle,
882                                     L"Element",
883                                     BiConvertElementFormatToValueType(
884                                     ElementType.Format),
885                                     &RegistryElementData,
886                                     &RegistryElementDataLength);
887         if (!NT_SUCCESS(Status))
888         {
889             EfiPrintf(L"Element invalid\r\n");
890             break;
891         }
892 
893         /* Now figure out how much space the converted element will need */
894         ElementDataLength = 0;
895         Status = BiConvertRegistryDataToElement(ObjectHandle,
896                                                 RegistryElementData,
897                                                 RegistryElementDataLength,
898                                                 ElementType,
899                                                 NULL,
900                                                 &ElementDataLength);
901         if (Status != STATUS_BUFFER_TOO_SMALL)
902         {
903             break;
904         }
905 
906         /* Allocate a buffer big enough for the converted element */
907         ElementData = BlMmAllocateHeap(ElementDataLength);
908         if (!ElementData)
909         {
910             Status = STATUS_INSUFFICIENT_RESOURCES;
911             break;
912         }
913 
914         /* And actually convert it this time around */
915         Status = BiConvertRegistryDataToElement(ObjectHandle,
916                                                 RegistryElementData,
917                                                 RegistryElementDataLength,
918                                                 ElementType,
919                                                 ElementData,
920                                                 &ElementDataLength);
921         if (!NT_SUCCESS(Status))
922         {
923             break;
924         }
925 
926         /* Safely add space for the packed element header */
927         Status = RtlULongAdd(TotalLength,
928                              FIELD_OFFSET(BCD_PACKED_ELEMENT, Data),
929                              &TotalLength);
930         if (!NT_SUCCESS(Status))
931         {
932             break;
933         }
934 
935         /* Safely add space for the data of the element itself */
936         Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength);
937         if (!NT_SUCCESS(Status))
938         {
939             break;
940         }
941 
942         /* One more element */
943         ++*ElementCount;
944 
945         /* See how much space we were given */
946         RemainingLength = *ElementSize;
947         if (RemainingLength >= TotalLength)
948         {
949             /* Set the next pointer */
950             Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + TotalLength);
951 
952             /* Fill this one out */
953             Elements->RootType.PackedValue = RootElementType;
954             Elements->Version = 1;
955             Elements->Type = ElementType.PackedValue;
956             Elements->Size = ElementDataLength;
957 
958             /* Add the data */
959             RtlCopyMemory(Elements->Data, ElementData, ElementDataLength);
960             RemainingLength -= TotalLength;
961 
962             /* Move to the next element on the next pass */
963             PreviousElement = Elements;
964             Elements = Elements->NextEntry;
965         }
966         else
967         {
968             /* We're out of space */
969             RemainingLength = 0;
970         }
971 
972         /* Are we enumerating devices, and is this a device? */
973         if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) &&
974             (ElementType.Format == BCD_TYPE_DEVICE))
975         {
976             /* Yep, so go inside to enumerate it */
977             Status = BiEnumerateSubElements(BcdHandle,
978                                             ElementData,
979                                             ElementType.PackedValue,
980                                             Flags,
981                                             &Elements,
982                                             &ElementDataLength,
983                                             &SubElementCount);
984             if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
985             {
986                 /* Safely add the length of the sub elements */
987                 Status = RtlULongAdd(TotalLength,
988                                      ElementDataLength,
989                                      &TotalLength);
990                 if (!NT_SUCCESS(Status))
991                 {
992                     break;
993                 }
994 
995                 /* Add the sub elements to the total */
996                 *ElementCount += SubElementCount;
997 
998                 /* See if we have enough space*/
999                 if (*ElementSize >= TotalLength)
1000                 {
1001                     /* Were there any subelements? */
1002                     if (SubElementCount)
1003                     {
1004                         /* Update to keep track of these new subelements */
1005                         ElementDataLength = *ElementSize - TotalLength;
1006 
1007                         /* Link the subelements into the chain */
1008                         PreviousElement = Elements;
1009                         PreviousElement->NextEntry =
1010                             (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart +
1011                                                   TotalLength);
1012                         Elements = PreviousElement->NextEntry;
1013                     }
1014                 }
1015                 else
1016                 {
1017                     /* We're out of space */
1018                     ElementDataLength = 0;
1019                 }
1020             }
1021             else if ((Status != STATUS_NOT_FOUND) &&
1022                      (Status != STATUS_OBJECT_NAME_NOT_FOUND))
1023             {
1024                 /* Fatal error trying to read the data, so fail */
1025                 break;
1026             }
1027         }
1028         else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) &&
1029                  (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects))
1030         {
1031             /* Inherited objects are requested, so allocate a buffer for them */
1032             SubObjectList = BlMmAllocateHeap(ElementDataLength);
1033             if (!SubObjectList)
1034             {
1035                 Status = STATUS_INSUFFICIENT_RESOURCES;
1036                 break;
1037             }
1038 
1039             /* Copy the elements into the list. They are arrays of GUIDs */
1040             RtlCopyMemory(SubObjectList, ElementData, ElementDataLength);
1041             SubObjectCount = ElementDataLength / sizeof(GUID);
1042         }
1043 
1044         /* Free our local buffers */
1045         BlMmFreeHeap(ElementData);
1046         BlMmFreeHeap(RegistryElementData);
1047         ElementData = NULL;
1048         RegistryElementData = NULL;
1049 
1050         /* Close the key */
1051         BiCloseKey(ElementHandle);
1052         ElementHandle = NULL;
1053         ElementName = NULL;
1054     }
1055 
1056     /* Did we end up here with a sub object list after successful loop parsing? */
1057     if ((i != 0) && (i == SubKeyCount) && (SubObjectList))
1058     {
1059         /* We will actually enumerate it now, at the end */
1060         Status = BiEnumerateSubObjectElements(BcdHandle,
1061                                               SubObjectList,
1062                                               SubObjectCount,
1063                                               Flags,
1064                                               Elements,
1065                                               &RemainingLength,
1066                                               &SubElementCount);
1067         if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
1068         {
1069             /* Safely add the length of the sub elements */
1070             Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength);
1071             if ((NT_SUCCESS(Status)) && (SubElementCount))
1072             {
1073                 /* Add the sub elements to the total */
1074                 *ElementCount += SubElementCount;
1075 
1076                 /* Don't touch PreviousElement anymore */
1077                 PreviousElement = NULL;
1078             }
1079         }
1080     }
1081 
1082 Quickie:
1083     /* Free the sub object list, if any */
1084     if (SubObjectList)
1085     {
1086         BlMmFreeHeap(SubObjectList);
1087     }
1088 
1089     /* Free any local element data */
1090     if (ElementData)
1091     {
1092         BlMmFreeHeap(ElementData);
1093     }
1094 
1095     /* Free any local registry data */
1096     if (RegistryElementData)
1097     {
1098         BlMmFreeHeap(RegistryElementData);
1099     }
1100 
1101     /* Close the handle if still opened */
1102     if (ElementHandle)
1103     {
1104         BiCloseKey(ElementHandle);
1105     }
1106 
1107     /* Terminate the last element, if any */
1108     if (PreviousElement)
1109     {
1110         PreviousElement->NextEntry = NULL;
1111     }
1112 
1113     /* Close the root handle if still opened */
1114     if (ElementsHandle)
1115     {
1116         BiCloseKey(ElementsHandle);
1117     }
1118 
1119     /* Set  failure code if out of space */
1120     if (*ElementSize < TotalLength)
1121     {
1122         Status = STATUS_BUFFER_TOO_SMALL;
1123     }
1124 
1125     /* Other errors will send a notification error */
1126     if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL))
1127     {
1128         BiNotifyEnumerationError(ObjectHandle, ElementName, Status);
1129     }
1130 
1131     /* Finally free the subkeys array */
1132     if (SubKeys)
1133     {
1134         BlMmFreeHeap(SubKeys);
1135     }
1136 
1137     /* And return the required, final length and status */
1138     *ElementSize = TotalLength;
1139     return Status;
1140 }
1141 
1142 NTSTATUS
1143 BiAddStoreFromFile (
1144     _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
1145     _Out_ PHANDLE StoreHandle
1146     )
1147 {
1148     NTSTATUS Status;
1149     HANDLE HiveHandle, KeyHandle;
1150 
1151     /* Load the specified hive */
1152     Status = BiLoadHive(FilePath, &HiveHandle);
1153     if (!NT_SUCCESS(Status))
1154     {
1155         return Status;
1156     }
1157 
1158     /* Open the description key to make sure this is really a BCD */
1159     Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
1160     if (NT_SUCCESS(Status))
1161     {
1162         /* It is -- close the key as we don't need it */
1163         BiCloseKey(KeyHandle);
1164         *StoreHandle = HiveHandle;
1165     }
1166     else
1167     {
1168         /* Failure, drop a reference on the hive and close the key */
1169         BiDereferenceHive(HiveHandle);
1170         BiCloseKey(HiveHandle);
1171     }
1172 
1173     /* Return the status */
1174     return Status;
1175 }
1176 
1177 NTSTATUS
1178 BiGetObjectDescription (
1179     _In_ HANDLE ObjectHandle,
1180     _Out_ PBCD_OBJECT_DESCRIPTION Description
1181     )
1182 {
1183     NTSTATUS Status;
1184     HANDLE DescriptionHandle;
1185     PULONG Data;
1186     ULONG Length;
1187 
1188     /* Initialize locals */
1189     Data = NULL;
1190     DescriptionHandle = NULL;
1191 
1192     /* Open the description key */
1193     Status = BiOpenKey(ObjectHandle, L"Description", &DescriptionHandle);
1194     if (NT_SUCCESS(Status))
1195     {
1196         /* It exists */
1197         Description->Valid = TRUE;
1198 
1199         /* Read the type */
1200         Length = 0;
1201         Status = BiGetRegistryValue(DescriptionHandle,
1202                                     L"Type",
1203                                     REG_DWORD,
1204                                     (PVOID*)&Data,
1205                                     &Length);
1206         if (NT_SUCCESS(Status))
1207         {
1208             /* Make sure it's the length we expected it to be */
1209             if (Length == sizeof(Data))
1210             {
1211                 /* Return the type that is stored there */
1212                 Description->Type = *Data;
1213             }
1214             else
1215             {
1216                 /* Invalid type value */
1217                 Status = STATUS_OBJECT_TYPE_MISMATCH;
1218             }
1219         }
1220     }
1221 
1222     /* Did we have a handle open? */
1223     if (DescriptionHandle)
1224     {
1225         /* Close it */
1226         BiCloseKey(DescriptionHandle);
1227     }
1228 
1229     /* Did we have data allocated? */
1230     if (Data)
1231     {
1232         /* Free it */
1233         BlMmFreeHeap(Data);
1234     }
1235 
1236     /* Return back to caller */
1237     return Status;
1238 }
1239 
1240 NTSTATUS
1241 BcdEnumerateAndUnpackElements (
1242     _In_ HANDLE BcdHandle,
1243     _In_ HANDLE ObjectHandle,
1244     _Out_opt_ PBCD_ELEMENT Elements,
1245     _Inout_ PULONG ElementSize,
1246     _Out_ PULONG ElementCount
1247     )
1248 {
1249     PVOID LocalElements;
1250     NTSTATUS Status;
1251     ULONG LocalElementCount, LocalElementSize;
1252 
1253     /* Make sure required parameters are there */
1254     if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize)))
1255     {
1256         return STATUS_INVALID_PARAMETER;
1257     }
1258 
1259     /* Set initial count to zero */
1260     *ElementCount = 0;
1261 
1262     /* Do the initial enumeration to figure out the size required */
1263     LocalElementSize = 0;
1264     LocalElementCount = 0;
1265     Status = BiEnumerateElements(BcdHandle,
1266                                  ObjectHandle,
1267                                  0,
1268                                  BCD_ENUMERATE_FLAG_IN_ORDER |
1269                                  BCD_ENUMERATE_FLAG_DEVICES |
1270                                  BCD_ENUMERATE_FLAG_DEEP,
1271                                  NULL,
1272                                  &LocalElementSize,
1273                                  &LocalElementCount);
1274     if (Status != STATUS_BUFFER_TOO_SMALL)
1275     {
1276         return Status;
1277     }
1278 
1279     /* Now allocate a buffer large enough to hold them */
1280     LocalElements = BlMmAllocateHeap(LocalElementSize);
1281     if (!LocalElements)
1282     {
1283         return STATUS_INSUFFICIENT_RESOURCES;
1284     }
1285 
1286     /* Zero out the array and do the real enumeration this time around */
1287     RtlZeroMemory(LocalElements, LocalElementSize);
1288     Status = BiEnumerateElements(BcdHandle,
1289                                  ObjectHandle,
1290                                  0,
1291                                  BCD_ENUMERATE_FLAG_IN_ORDER |
1292                                  BCD_ENUMERATE_FLAG_DEVICES |
1293                                  BCD_ENUMERATE_FLAG_DEEP,
1294                                  LocalElements,
1295                                  &LocalElementSize,
1296                                  &LocalElementCount);
1297     if (!NT_SUCCESS(Status))
1298     {
1299         return Status;
1300     }
1301 
1302     /* Now we know the real count */
1303     *ElementCount = LocalElementCount;
1304 
1305     /* Now unpack the data */
1306     Status = BiConvertBcdElements(LocalElements,
1307                                   Elements,
1308                                   ElementSize,
1309                                   &LocalElementCount);
1310     if (NT_SUCCESS(Status))
1311     {
1312         /* Not all elements may have been converted */
1313         *ElementCount = LocalElementCount;
1314     }
1315 
1316     /* Free the local (unpacked) buffer and return status */
1317     BlMmFreeHeap(LocalElements);
1318     return Status;
1319 }
1320 
1321 NTSTATUS
1322 BcdOpenStoreFromFile (
1323     _In_ PUNICODE_STRING FileName,
1324     _In_ PHANDLE BcdHandle
1325     )
1326 {
1327     ULONG Length;
1328     PBL_FILE_PATH_DESCRIPTOR FilePath;
1329     NTSTATUS Status;
1330     HANDLE LocalHandle;
1331 
1332     /* Assume failure */
1333     LocalHandle = NULL;
1334 
1335     /* Allocate a path descriptor */
1336     Length = FileName->Length + sizeof(*FilePath);
1337     FilePath = BlMmAllocateHeap(Length);
1338     if (!FilePath)
1339     {
1340         return STATUS_NO_MEMORY;
1341     }
1342 
1343     /* Initialize it */
1344     FilePath->Version = 1;
1345     FilePath->PathType = InternalPath;
1346     FilePath->Length = Length;
1347 
1348     /* Copy the name and NULL-terminate it */
1349     RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
1350     FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
1351 
1352     /* Open the BCD */
1353     Status = BiAddStoreFromFile(FilePath, &LocalHandle);
1354     if (NT_SUCCESS(Status))
1355     {
1356         /* Return the handle on success */
1357         *BcdHandle = LocalHandle;
1358     }
1359 
1360     /* Free the descriptor and return the status */
1361     BlMmFreeHeap(FilePath);
1362     return Status;
1363 }
1364 
1365