[BOOTLIB]: Separate out bcd.c into bcdopt.c, bootreg.c and bcd.c
[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\r\n");
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 BiEnumerateSubElements (
561 _In_ HANDLE BcdHandle,
562 _In_ PVOID Object,
563 _In_ ULONG ElementType,
564 _In_ ULONG Flags,
565 _Out_opt_ PBCD_PACKED_ELEMENT* Elements,
566 _Inout_ PULONG ElementSize,
567 _Out_ PULONG ElementCount
568 )
569 {
570 NTSTATUS Status;
571 PBCD_PACKED_ELEMENT Element;
572 HANDLE ObjectHandle;
573 ULONG ParsedElements, RequiredSize;
574
575 /* Assume empty */
576 *ElementCount = 0;
577 RequiredSize = 0;
578 ParsedElements = 0;
579
580 /* Open the object */
581 Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle);
582 if (!NT_SUCCESS(Status))
583 {
584 goto Quickie;
585 }
586
587 /* Read the first entry, and the size available */
588 Element = *Elements;
589 RequiredSize = *ElementSize;
590
591 /* Enumerate the object into the element array */
592 Status = BiEnumerateElements(BcdHandle,
593 ObjectHandle,
594 ElementType,
595 Flags,
596 Element,
597 &RequiredSize,
598 &ParsedElements);
599
600 /* Close the handle and bail out if we couldn't enumerate */
601 BiCloseKey(ObjectHandle);
602 if (!NT_SUCCESS(Status))
603 {
604 goto Quickie;
605 }
606
607 /* Check if the and subelements were present */
608 if (ParsedElements)
609 {
610 /* Keep going until the last one */
611 while (Element->NextEntry)
612 {
613 Element = Element->NextEntry;
614 }
615
616 /* Set the new buffer location to the last element */
617 *Elements = Element;
618 }
619
620 Quickie:
621 /* Return the number of sub-elements and their size */
622 *ElementCount = ParsedElements;
623 *ElementSize = RequiredSize;
624 return Status;
625 }
626
627 NTSTATUS
628 BiEnumerateSubObjectElements (
629 _In_ HANDLE BcdHandle,
630 _Out_ PGUID SubObjectList,
631 _In_ ULONG SubObjectCount,
632 _In_ ULONG Flags,
633 _Out_opt_ PBCD_PACKED_ELEMENT Elements,
634 _Inout_ PULONG ElementSize,
635 _Out_ PULONG ElementCount
636 )
637 {
638 NTSTATUS Status;
639 ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i;
640 PBCD_PACKED_ELEMENT PreviousElement;
641
642 /* Assume empty list */
643 *ElementCount = 0;
644 Status = STATUS_SUCCESS;
645
646 /* Initialize variables */
647 TotalSize = 0;
648 PreviousElement = NULL;
649
650 /* Set the currently remaining size based on caller's input */
651 CurrentSize = *ElementSize;
652
653 /* Iterate over every subje object */
654 for (i = 0; i < SubObjectCount; i++)
655 {
656 /* Set the currently remaining buffer space */
657 RequiredSize = CurrentSize;
658
659 /* Enumerate the inherited sub elements */
660 Status = BiEnumerateSubElements(BcdHandle,
661 &SubObjectList[i],
662 BcdLibraryObjectList_InheritedObjects,
663 Flags,
664 &Elements,
665 &RequiredSize,
666 &SubElementCount);
667 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
668 {
669 /* Safely add the length of the sub elements */
670 Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize);
671 if (!NT_SUCCESS(Status))
672 {
673 break;
674 }
675
676 /* Add the sub elements to the total */
677 *ElementCount += SubElementCount;
678
679 /* See if we have enough space*/
680 if (*ElementSize >= TotalSize)
681 {
682 /* Were there any subelements? */
683 if (SubElementCount)
684 {
685 /* Update to keep track of these new subelements */
686 CurrentSize = *ElementSize - TotalSize;
687
688 /* Link the subelements into the chain */
689 PreviousElement = Elements;
690 PreviousElement->NextEntry =
691 (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize);
692 Elements = PreviousElement->NextEntry;
693 }
694 }
695 else
696 {
697 /* We're out of space */
698 CurrentSize = 0;
699 }
700 }
701 else if ((Status != STATUS_NOT_FOUND) &&
702 (Status != STATUS_OBJECT_NAME_NOT_FOUND))
703 {
704 /* Some other fatal error, break out */
705 break;
706 }
707 else
708 {
709 /* The sub element was not found, print a warning but keep going */
710 BlStatusPrint(L"Ignoring missing BCD inherit object: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
711 (&SubObjectList[i])->Data1,
712 (&SubObjectList[i])->Data2,
713 (&SubObjectList[i])->Data3,
714 (&SubObjectList[i])->Data4[0],
715 (&SubObjectList[i])->Data4[1],
716 (&SubObjectList[i])->Data4[2],
717 (&SubObjectList[i])->Data4[3],
718 (&SubObjectList[i])->Data4[4],
719 (&SubObjectList[i])->Data4[5],
720 (&SubObjectList[i])->Data4[6],
721 (&SubObjectList[i])->Data4[7],
722 (&SubObjectList[i])->Data4[8]);
723 Status = STATUS_SUCCESS;
724 }
725 }
726
727 /* Terminate the last element, if one was left */
728 if (PreviousElement)
729 {
730 PreviousElement->NextEntry = NULL;
731 }
732
733 /* Set failure code if we ran out of space */
734 if (*ElementSize < TotalSize)
735 {
736 Status = STATUS_BUFFER_TOO_SMALL;
737 }
738
739 /* Return final length and status */
740 *ElementSize = TotalSize;
741 return Status;
742 }
743
744 NTSTATUS
745 BiEnumerateElements (
746 _In_ HANDLE BcdHandle,
747 _In_ HANDLE ObjectHandle,
748 _In_ ULONG RootElementType,
749 _In_ ULONG Flags,
750 _Out_opt_ PBCD_PACKED_ELEMENT Elements,
751 _Inout_ PULONG ElementSize,
752 _Out_ PULONG ElementCount
753 )
754 {
755 HANDLE ElementsHandle, ElementHandle;
756 ULONG TotalLength, RegistryElementDataLength, RemainingLength;
757 NTSTATUS Status;
758 ULONG i;
759 PVOID ElementData, SubObjectList, RegistryElementData;
760 BcdElementType ElementType;
761 PBCD_PACKED_ELEMENT PreviousElement;
762 ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength;
763 PWCHAR ElementName;
764 PWCHAR* SubKeys;
765
766 /* Assume failure */
767 *ElementCount = 0;
768
769 /* Initialize all locals that are checked at the end*/
770 SubKeys = NULL;
771 ElementsHandle = NULL;
772 ElementHandle = NULL;
773 ElementData = NULL;
774 RegistryElementData = NULL;
775 PreviousElement = NULL;
776 ElementName = NULL;
777 SubObjectList = NULL;
778 TotalLength = 0;
779 ElementDataLength = 0;
780 SubObjectCount = 0;
781 RemainingLength = 0;
782
783 /* Open the root object key's elements */
784 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
785 if (!NT_SUCCESS(Status))
786 {
787 goto Quickie;
788 }
789
790 /* Enumerate all elements */
791 Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount);
792 if (!NT_SUCCESS(Status))
793 {
794 goto Quickie;
795 }
796
797 /* Iterate over each one */
798 for (i = 0; i < SubKeyCount; i++)
799 {
800 /* Open the element */
801 ElementName = SubKeys[i];
802 Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle);
803 if (!NT_SUCCESS(Status))
804 {
805 break;
806 }
807
808 /* The name of the element is its data type */
809 ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16);
810 if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1))
811 {
812 EfiPrintf(L"Value invald\r\n");
813 BiCloseKey(ElementHandle);
814 ElementHandle = 0;
815 continue;
816 }
817
818 /* Read the appropriate registry value type for this element */
819 Status = BiGetRegistryValue(ElementHandle,
820 L"Element",
821 NULL,
822 BiConvertElementFormatToValueType(
823 ElementType.Format),
824 &RegistryElementData,
825 &RegistryElementDataLength);
826 if (!NT_SUCCESS(Status))
827 {
828 break;
829 }
830
831 /* Now figure out how much space the converted element will need */
832 ElementDataLength = 0;
833 Status = BiConvertRegistryDataToElement(ObjectHandle,
834 RegistryElementData,
835 RegistryElementDataLength,
836 ElementType,
837 NULL,
838 &ElementDataLength);
839 if (Status != STATUS_BUFFER_TOO_SMALL)
840 {
841 break;
842 }
843
844 /* Allocate a buffer big enough for the converted element */
845 ElementData = BlMmAllocateHeap(ElementDataLength);
846 if (!ElementData)
847 {
848 Status = STATUS_INSUFFICIENT_RESOURCES;
849 break;
850 }
851
852 /* And actually convert it this time around */
853 Status = BiConvertRegistryDataToElement(ObjectHandle,
854 RegistryElementData,
855 RegistryElementDataLength,
856 ElementType,
857 ElementData,
858 &ElementDataLength);
859 if (!NT_SUCCESS(Status))
860 {
861 break;
862 }
863
864 /* Safely add space for the packed element header */
865 Status = RtlULongAdd(TotalLength,
866 FIELD_OFFSET(BCD_PACKED_ELEMENT, Data),
867 &TotalLength);
868 if (!NT_SUCCESS(Status))
869 {
870 break;
871 }
872
873 /* Safely add space for the data of the element itself */
874 Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength);
875 if (!NT_SUCCESS(Status))
876 {
877 break;
878 }
879
880 /* One more element */
881 ++*ElementCount;
882
883 /* See how much space we were given */
884 RemainingLength = *ElementSize;
885 if (RemainingLength >= TotalLength)
886 {
887 /* Set the next pointer */
888 Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalLength);
889
890 /* Fill this one out */
891 Elements->RootType.PackedValue = RootElementType;
892 Elements->Version = 1;
893 Elements->Type = ElementType.PackedValue;
894 Elements->Size = ElementDataLength;
895
896 /* Add the data */
897 RtlCopyMemory(Elements->Data, ElementData, ElementDataLength);
898 RemainingLength -= TotalLength;
899
900 /* Move to the next element on the next pass */
901 PreviousElement = Elements;
902 Elements = Elements->NextEntry;
903 }
904 else
905 {
906 /* We're out of space */
907 RemainingLength = 0;
908 }
909
910 /* Are we enumerating devices, and is this a device? */
911 if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) &&
912 (ElementType.Format == BCD_TYPE_DEVICE))
913 {
914 /* Yep, so go inside to enumerate it */
915 Status = BiEnumerateSubElements(BcdHandle,
916 ElementData,
917 ElementType.PackedValue,
918 Flags,
919 &Elements,
920 &ElementDataLength,
921 &SubElementCount);
922 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
923 {
924 /* Safely add the length of the sub elements */
925 Status = RtlULongAdd(TotalLength,
926 ElementDataLength,
927 &TotalLength);
928 if (!NT_SUCCESS(Status))
929 {
930 break;
931 }
932
933 /* Add the sub elements to the total */
934 *ElementCount += SubElementCount;
935
936 /* See if we have enough space*/
937 if (*ElementSize >= TotalLength)
938 {
939 /* Were there any subelements? */
940 if (SubElementCount)
941 {
942 /* Update to keep track of these new subelements */
943 ElementDataLength = *ElementSize - TotalLength;
944
945 /* Link the subelements into the chain */
946 PreviousElement = Elements;
947 PreviousElement->NextEntry =
948 (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements +
949 TotalLength);
950 Elements = PreviousElement->NextEntry;
951 }
952 }
953 else
954 {
955 /* We're out of space */
956 ElementDataLength = 0;
957 }
958 }
959 else if ((Status != STATUS_NOT_FOUND) &&
960 (Status != STATUS_OBJECT_NAME_NOT_FOUND))
961 {
962 /* Fatal error trying to read the data, so fail */
963 break;
964 }
965 }
966 else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) &&
967 (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects))
968 {
969 /* Inherited objects are requsted, so allocate a buffer for them */
970 SubObjectList = BlMmAllocateHeap(ElementDataLength);
971 if (!SubObjectList)
972 {
973 Status = STATUS_INSUFFICIENT_RESOURCES;
974 break;
975 }
976
977 /* Copy the elements into the list. They are arrays of GUIDs */
978 RtlCopyMemory(SubObjectList, ElementData, ElementDataLength);
979 SubObjectCount = ElementDataLength / sizeof(GUID);
980 }
981
982 /* Free our local buffers */
983 BlMmFreeHeap(ElementData);
984 BlMmFreeHeap(RegistryElementData);
985 ElementData = NULL;
986 RegistryElementData = NULL;
987
988 /* Close the key */
989 BiCloseKey(ElementHandle);
990 ElementHandle = NULL;
991 ElementName = NULL;
992 }
993
994 /* Did we end up here with a sub object list after successful loop parsing? */
995 if ((i != 0) && (i == SubKeyCount) && (SubObjectList))
996 {
997 /* We will actually enumerate it now, at the end */
998 Status = BiEnumerateSubObjectElements(BcdHandle,
999 SubObjectList,
1000 SubObjectCount,
1001 Flags,
1002 Elements,
1003 &RemainingLength,
1004 &SubElementCount);
1005 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
1006 {
1007 /* Safely add the length of the sub elements */
1008 Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength);
1009 if ((NT_SUCCESS(Status)) && (SubElementCount))
1010 {
1011 /* Add the sub elements to the total */
1012 *ElementCount += SubElementCount;
1013
1014 /* Don't touch PreviousElement anymore */
1015 PreviousElement = NULL;
1016 }
1017 }
1018 }
1019
1020 Quickie:
1021 /* Free the sub object list, if any */
1022 if (SubObjectList)
1023 {
1024 BlMmFreeHeap(SubObjectList);
1025 }
1026
1027 /* Free any local element data */
1028 if (ElementData)
1029 {
1030 BlMmFreeHeap(ElementData);
1031 }
1032
1033 /* Free any local registry data */
1034 if (RegistryElementData)
1035 {
1036 BlMmFreeHeap(RegistryElementData);
1037 }
1038
1039 /* Close the handle if still opened */
1040 if (ElementHandle)
1041 {
1042 BiCloseKey(ElementHandle);
1043 }
1044
1045 /* Terminate the last element, if any */
1046 if (PreviousElement)
1047 {
1048 PreviousElement->NextEntry = NULL;
1049 }
1050
1051 /* Close the root handle if still opened */
1052 if (ElementsHandle)
1053 {
1054 BiCloseKey(ElementsHandle);
1055 }
1056
1057 /* Set failure code if out of space */
1058 if (*ElementSize < TotalLength)
1059 {
1060 Status = STATUS_BUFFER_TOO_SMALL;
1061 }
1062
1063 /* Other errors will send a notification error */
1064 if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL))
1065 {
1066 BiNotifyEnumerationError(ObjectHandle, ElementName, Status);
1067 }
1068
1069 /* Finally free the subkeys array */
1070 if (SubKeys)
1071 {
1072 BlMmFreeHeap(SubKeys);
1073 }
1074
1075 /* And return the required, final length and status */
1076 *ElementSize = TotalLength;
1077 return Status;
1078 }
1079
1080 NTSTATUS
1081 BiAddStoreFromFile (
1082 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
1083 _Out_ PHANDLE StoreHandle
1084 )
1085 {
1086 NTSTATUS Status;
1087 HANDLE HiveHandle, KeyHandle;
1088
1089 /* Load the specified hive */
1090 Status = BiLoadHive(FilePath, &HiveHandle);
1091 if (!NT_SUCCESS(Status))
1092 {
1093 return Status;
1094 }
1095
1096 /* Open the description key to make sure this is really a BCD */
1097 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
1098 if (NT_SUCCESS(Status))
1099 {
1100 /* It is -- close the key as we don't need it */
1101 BiCloseKey(KeyHandle);
1102 *StoreHandle = HiveHandle;
1103 }
1104 else
1105 {
1106 /* Failure, drop a reference on the hive and close the key */
1107 BiDereferenceHive(HiveHandle);
1108 BiCloseKey(HiveHandle);
1109 }
1110
1111 /* Return the status */
1112 return Status;
1113 }
1114
1115 NTSTATUS
1116 BcdEnumerateAndUnpackElements (
1117 _In_ HANDLE BcdHandle,
1118 _In_ HANDLE ObjectHandle,
1119 _Out_opt_ PBCD_ELEMENT Elements,
1120 _Inout_ PULONG ElementSize,
1121 _Out_ PULONG ElementCount
1122 )
1123 {
1124 PVOID LocalElements;
1125 NTSTATUS Status;
1126 ULONG LocalElementCount, LocalElementSize;
1127
1128 /* Make sure required parameters are there */
1129 if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize)))
1130 {
1131 return STATUS_INVALID_PARAMETER;
1132 }
1133
1134 /* Set initial count to zero */
1135 *ElementCount = 0;
1136
1137 /* Do the initial enumeration to figure out the size required */
1138 LocalElementSize = 0;
1139 LocalElementCount = 0;
1140 Status = BiEnumerateElements(BcdHandle,
1141 ObjectHandle,
1142 0,
1143 BCD_ENUMERATE_FLAG_IN_ORDER |
1144 BCD_ENUMERATE_FLAG_DEVICES |
1145 BCD_ENUMERATE_FLAG_DEEP,
1146 NULL,
1147 &LocalElementSize,
1148 &LocalElementCount);
1149 if (Status != STATUS_BUFFER_TOO_SMALL)
1150 {
1151 return Status;
1152 }
1153
1154 /* Now allocate a buffer large enough to hold them */
1155 LocalElements = BlMmAllocateHeap(LocalElementSize);
1156 if (!LocalElements)
1157 {
1158 return STATUS_INSUFFICIENT_RESOURCES;
1159 }
1160
1161 /* Zero out the array and do the real enumeration this time around */
1162 RtlZeroMemory(LocalElements, LocalElementSize);
1163 Status = BiEnumerateElements(BcdHandle,
1164 ObjectHandle,
1165 0,
1166 BCD_ENUMERATE_FLAG_IN_ORDER |
1167 BCD_ENUMERATE_FLAG_DEVICES |
1168 BCD_ENUMERATE_FLAG_DEEP,
1169 LocalElements,
1170 &LocalElementSize,
1171 &LocalElementCount);
1172 if (!NT_SUCCESS(Status))
1173 {
1174 return Status;
1175 }
1176
1177 /* Now we know the real count */
1178 *ElementCount = LocalElementCount;
1179
1180 /* Now unpack the data */
1181 Status = BiConvertBcdElements(LocalElements,
1182 Elements,
1183 ElementSize,
1184 &LocalElementCount);
1185 if (NT_SUCCESS(Status))
1186 {
1187 /* Not all elements may have been converted */
1188 *ElementCount = LocalElementCount;
1189 }
1190
1191 /* Free the local (unpacked) buffer and return status */
1192 BlMmFreeHeap(LocalElements);
1193 return Status;
1194 }
1195
1196 NTSTATUS
1197 BcdOpenStoreFromFile (
1198 _In_ PUNICODE_STRING FileName,
1199 _In_ PHANDLE BcdHandle
1200 )
1201 {
1202 ULONG Length;
1203 PBL_FILE_PATH_DESCRIPTOR FilePath;
1204 NTSTATUS Status;
1205 HANDLE LocalHandle;
1206
1207 /* Assume failure */
1208 LocalHandle = NULL;
1209
1210 /* Allocate a path descriptor */
1211 Length = FileName->Length + sizeof(*FilePath);
1212 FilePath = BlMmAllocateHeap(Length);
1213 if (!FilePath)
1214 {
1215 return STATUS_NO_MEMORY;
1216 }
1217
1218 /* Initialize it */
1219 FilePath->Version = 1;
1220 FilePath->PathType = InternalPath;
1221 FilePath->Length = Length;
1222
1223 /* Copy the name and NULL-terminate it */
1224 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
1225 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
1226
1227 /* Open the BCD */
1228 Status = BiAddStoreFromFile(FilePath, &LocalHandle);
1229 if (NT_SUCCESS(Status))
1230 {
1231 /* Return the handle on success */
1232 *BcdHandle = LocalHandle;
1233 }
1234
1235 /* Free the descriptor and return the status */
1236 BlMmFreeHeap(FilePath);
1237 return Status;
1238 }
1239