[CRT] Massively improve performance of rand_s
[reactos.git] / 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 SIZE_T 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