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