[BOOTLIB]:
[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 PBL_BCD_OPTION
17 MiscGetBootOption (
18 _In_ PBL_BCD_OPTION List,
19 _In_ ULONG Type
20 )
21 {
22 ULONG_PTR NextOption = 0, ListOption;
23 PBL_BCD_OPTION Option, FoundOption;
24
25 /* No options, bail out */
26 if (!List)
27 {
28 return NULL;
29 }
30
31 /* Loop while we find an option */
32 FoundOption = NULL;
33 do
34 {
35 /* Get the next option and see if it matches the type */
36 Option = (PBL_BCD_OPTION)((ULONG_PTR)List + NextOption);
37 if ((Option->Type == Type) && !(Option->Empty))
38 {
39 FoundOption = Option;
40 break;
41 }
42
43 /* Store the offset of the next option */
44 NextOption = Option->NextEntryOffset;
45
46 /* Failed to match. Check for list options */
47 ListOption = Option->ListOffset;
48 if (ListOption)
49 {
50 /* Try to get a match in the associated option */
51 Option = MiscGetBootOption((PBL_BCD_OPTION)((ULONG_PTR)Option +
52 ListOption),
53 Type);
54 if (Option)
55 {
56 /* Return it */
57 FoundOption = Option;
58 break;
59 }
60 }
61 } while (NextOption);
62
63 /* Return the option that was found, if any */
64 return FoundOption;
65 }
66
67 /*++
68 * @name BlGetBootOptionListSize
69 *
70 * The BlGetBootOptionListSize routine
71 *
72 * @param BcdOption
73 * UEFI Image Handle for the current loaded application.
74 *
75 * @return Size of the BCD option
76 *
77 *--*/
78 ULONG
79 BlGetBootOptionListSize (
80 _In_ PBL_BCD_OPTION BcdOption
81 )
82 {
83 ULONG Size = 0, NextOffset = 0;
84 PBL_BCD_OPTION NextOption;
85
86 /* Loop all the options*/
87 do
88 {
89 /* Move to the next one */
90 NextOption = (PBL_BCD_OPTION)((ULONG_PTR)BcdOption + NextOffset);
91
92 /* Compute the size of the next one */
93 Size += BlGetBootOptionSize(NextOption);
94
95 /* Update the offset */
96 NextOffset = NextOption->NextEntryOffset;
97 } while (NextOffset);
98
99 /* Return final computed size */
100 return Size;
101 }
102
103 /*++
104 * @name BlGetBootOptionSize
105 *
106 * The BlGetBootOptionSize routine
107 *
108 * @param BcdOption
109 * UEFI Image Handle for the current loaded application.
110 *
111 * @return Size of the BCD option
112 *
113 *--*/
114 ULONG
115 BlGetBootOptionSize (
116 _In_ PBL_BCD_OPTION BcdOption
117 )
118 {
119 ULONG Size, Offset;
120
121 /* Check if there's any data */
122 if (BcdOption->DataOffset)
123 {
124 /* Add the size of the data */
125 Size = BcdOption->DataOffset + BcdOption->DataSize;
126 }
127 else
128 {
129 /* No data, just the structure itself */
130 Size = sizeof(*BcdOption);
131 }
132
133 /* Any associated options? */
134 Offset = BcdOption->ListOffset;
135 if (Offset)
136 {
137 /* Go get those too */
138 Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset));
139 }
140
141 /* Return the final size */
142 return Size;
143 }
144
145 NTSTATUS
146 BlGetBootOptionString (
147 _In_ PBL_BCD_OPTION List,
148 _In_ ULONG Type,
149 _Out_ PWCHAR* Value
150 )
151 {
152 NTSTATUS Status;
153 PBL_BCD_OPTION Option;
154 PWCHAR String, StringCopy;
155 ULONG StringLength, CopyLength;
156 //PGUID AppIdentifier;
157
158 /* Make sure this is a BCD_STRING */
159 if ((Type & 0xF000000) != 0x2000000)
160 {
161 return STATUS_INVALID_PARAMETER;
162 }
163
164 /* Return the data */
165 Option = MiscGetBootOption(List, Type);
166 if (Option)
167 {
168 /* Extract the string */
169 String = (PWCHAR)((ULONG_PTR)Option + Option->DataOffset);
170 Status = STATUS_SUCCESS;
171 }
172 else
173 {
174 /* No string is present */
175 String = NULL;
176 Status = STATUS_NOT_FOUND;
177 }
178
179 /* Compute the data size */
180 StringLength = Option->DataSize / sizeof(WCHAR);
181
182 #ifdef _SECURE_BOOT_
183 /* Filter out SecureBoot Options */
184 AppIdentifier = BlGetApplicationIdentifier();
185 Status = BlpBootOptionCallbackString(AppIdentifier, Type, String, StringLength, &String, &StringLength);
186 #else
187 #endif
188 /* Check if we have space for one more character */
189 CopyLength = StringLength + 1;
190 if (CopyLength < StringLength)
191 {
192 /* Nope, we'll overflow */
193 CopyLength = -1;
194 Status = STATUS_INTEGER_OVERFLOW;
195 }
196
197 /* No overflow? */
198 if (NT_SUCCESS(Status))
199 {
200 /* Check if it's safe to multiply by two */
201 if ((CopyLength * sizeof(WCHAR)) > 0xFFFFFFFF)
202 {
203 /* Nope */
204 CopyLength = -1;
205 Status = STATUS_INTEGER_OVERFLOW;
206 }
207 else
208 {
209 /* We're good, do the multiplication */
210 Status = STATUS_SUCCESS;
211 CopyLength *= sizeof(WCHAR);
212 }
213
214 /* Allocate a copy for the string */
215 if (NT_SUCCESS(Status))
216 {
217 StringCopy = BlMmAllocateHeap(CopyLength);
218 if (StringCopy)
219 {
220 /* NULL-terminate it */
221 RtlCopyMemory(StringCopy,
222 String,
223 CopyLength - sizeof(UNICODE_NULL));
224 StringCopy[CopyLength] = UNICODE_NULL;
225 *Value = StringCopy;
226 Status = STATUS_SUCCESS;
227 }
228 else
229 {
230 /* No memory, fail */
231 Status = STATUS_NO_MEMORY;
232 }
233 }
234 }
235
236 /* All done */
237 return Status;
238 }
239
240 NTSTATUS
241 BlGetBootOptionDevice (
242 _In_ PBL_BCD_OPTION List,
243 _In_ ULONG Type,
244 _Out_ PBL_DEVICE_DESCRIPTOR* Value,
245 _In_opt_ PBL_BCD_OPTION* ExtraOptions
246 )
247 {
248 NTSTATUS Status;
249 PBL_BCD_OPTION Option, ListData, ListCopy, SecureListData;
250 PBCD_DEVICE_OPTION BcdDevice;
251 ULONG DeviceSize, ListOffset, ListSize;
252 PBL_DEVICE_DESCRIPTOR DeviceDescriptor, SecureDescriptor;
253 //PGUID AppIdentifier;
254
255 /* Make sure this is a BCD_DEVICE */
256 if ((Type & 0xF000000) != 0x1000000)
257 {
258 return STATUS_INVALID_PARAMETER;
259 }
260
261 /* Return the data */
262 Option = MiscGetBootOption(List, Type);
263 if (!Option)
264 {
265 /* Set failure if no data exists */
266 Status = STATUS_NOT_FOUND;
267 }
268 else
269 {
270 /* Otherwise, read the size of the BCD device encoded */
271 BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)Option + Option->DataOffset);
272 DeviceSize = BcdDevice->DeviceDescriptor.Size;
273
274 /* Allocate a buffer to copy it into */
275 DeviceDescriptor = BlMmAllocateHeap(DeviceSize);
276 if (!DeviceDescriptor)
277 {
278 return STATUS_NO_MEMORY;
279 }
280
281 /* Copy it into that buffer */
282 RtlCopyMemory(DeviceDescriptor, &BcdDevice->DeviceDescriptor, DeviceSize);
283 Status = STATUS_SUCCESS;
284 }
285
286 /* Check if extra options were requested */
287 if (ExtraOptions)
288 {
289 /* See where they are */
290 ListOffset = Option->ListOffset;
291 if (ListOffset)
292 {
293 /* See how big they are */
294 ListData = (PBL_BCD_OPTION)((ULONG_PTR)Option + ListOffset);
295 ListSize = BlGetBootOptionListSize(ListData);
296
297 /* Allocate a buffer to hold them into */
298 ListCopy = BlMmAllocateHeap(ListSize);
299 if (!ListCopy)
300 {
301 Status = STATUS_NO_MEMORY;
302 goto Quickie;
303 }
304
305 /* Copy them in there */
306 RtlCopyMemory(ListCopy, ListData, ListSize);
307 }
308 }
309
310 #ifdef _SECURE_BOOT_
311 /* Filter out SecureBoot Options */
312 AppIdentifier = BlGetApplicationIdentifier();
313 if (BlpBootOptionCallbacks)
314 {
315 DeviceCallback = BlpBootOptionCallbacks->Device;
316 if (DeviceCallback)
317 {
318 Status = DeviceCallback(BlpBootOptionCallbackCookie,
319 Status,
320 0,
321 AppIdentifier,
322 Type,
323 &SecureDescriptor,
324 PtrOptionData);
325 }
326 }
327 #else
328 /* No secure boot, so the secure descriptors are the standard ones */
329 SecureDescriptor = DeviceDescriptor;
330 SecureListData = ListCopy;
331 #endif
332
333 /* Check if the data was read correctly */
334 if (NT_SUCCESS(Status))
335 {
336 /* Check if we had a new descriptor after filtering */
337 if (SecureDescriptor != DeviceDescriptor)
338 {
339 /* Yep -- if we had an old one, free it */
340 if (DeviceDescriptor)
341 {
342 BlMmFreeHeap(DeviceDescriptor);
343 }
344 }
345
346 /* Check if we had a new list after filtering */
347 if (SecureListData != ListCopy)
348 {
349 /* Yep -- if we had an old list, free it */
350 if (ListCopy)
351 {
352 BlMmFreeHeap(ListCopy);
353 }
354 }
355
356 /* Finally, check if the caller wanted extra options */
357 if (ExtraOptions)
358 {
359 /* Yep -- so pass the caller our copy */
360 *ExtraOptions = ListCopy;
361 ListCopy = NULL;
362 }
363
364 /* Caller always wants data back, so pass them our copy */
365 *Value = DeviceDescriptor;
366 DeviceDescriptor = NULL;
367 }
368
369 Quickie:
370 /* On the failure path, if these buffers are active, we should free them */
371 if (ListCopy)
372 {
373 BlMmFreeHeap(ListCopy);
374 }
375 if (DeviceDescriptor)
376 {
377 BlMmFreeHeap(DeviceDescriptor);
378 }
379
380 /* All done */
381 return Status;
382 }
383
384 NTSTATUS
385 BlGetBootOptionInteger (
386 _In_ PBL_BCD_OPTION List,
387 _In_ ULONG Type,
388 _Out_ PULONGLONG Value
389 )
390 {
391 NTSTATUS Status;
392 PBL_BCD_OPTION Option;
393 //PGUID AppIdentifier;
394
395 /* Make sure this is a BCD_INTEGER */
396 if ((Type & 0xF000000) != 0x5000000)
397 {
398 return STATUS_INVALID_PARAMETER;
399 }
400
401 /* Return the data */
402 Option = MiscGetBootOption(List, Type);
403 if (Option)
404 {
405 *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
406 }
407
408 #ifdef _SECURE_BOOT_
409 /* Filter out SecureBoot Options */
410 AppIdentifier = BlGetApplicationIdentifier();
411 Status = BlpBootOptionCallbackULongLong(AppIdentifier, Type, Value);
412 #else
413 /* Option found */
414 Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
415 #endif
416 return Status;
417 }
418
419 NTSTATUS
420 BlGetBootOptionBoolean (
421 _In_ PBL_BCD_OPTION List,
422 _In_ ULONG Type,
423 _Out_ PBOOLEAN Value
424 )
425 {
426 NTSTATUS Status;
427 PBL_BCD_OPTION Option;
428 //PGUID AppIdentifier;
429
430 /* Make sure this is a BCD_BOOLEAN */
431 if ((Type & 0xF000000) != 0x6000000)
432 {
433 return STATUS_INVALID_PARAMETER;
434 }
435
436 /* Return the data */
437 Option = MiscGetBootOption(List, Type);
438 if (Option)
439 {
440 *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset);
441 }
442
443 #ifdef _SECURE_BOOT_
444 /* Filter out SecureBoot Options */
445 AppIdentifier = BlGetApplicationIdentifier();
446 Status = BlpBootOptionCallbackBoolean(AppIdentifier, Type, Value);
447 #else
448 /* Option found */
449 Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
450 #endif
451 return Status;
452 }
453
454 #define BI_FLUSH_HIVE 0x01
455 #define BI_HIVE_WRITEABLE 0x02
456
457 typedef struct _BI_KEY_HIVE
458 {
459 PHBASE_BLOCK BaseBlock;
460 ULONG HiveSize;
461 PBL_FILE_PATH_DESCRIPTOR FilePath;
462 CMHIVE Hive;
463 LONG ReferenceCount;
464 ULONG Flags;
465 PCM_KEY_NODE RootNode;
466 } BI_KEY_HIVE, *PBI_KEY_HIVE;
467
468 typedef struct _BI_KEY_OBJECT
469 {
470 PBI_KEY_HIVE KeyHive;
471 PCM_KEY_NODE KeyNode;
472 HCELL_INDEX KeyCell;
473 PWCHAR KeyName;
474 } BI_KEY_OBJECT, *PBI_KEY_OBJECT;
475
476 PVOID
477 NTAPI
478 CmpAllocate (
479 _In_ SIZE_T Size,
480 _In_ BOOLEAN Paged,
481 _In_ ULONG Tag
482 )
483 {
484 UNREFERENCED_PARAMETER(Paged);
485 UNREFERENCED_PARAMETER(Tag);
486
487 /* Call the heap allocator */
488 return BlMmAllocateHeap(Size);
489 }
490
491 VOID
492 NTAPI
493 CmpFree (
494 _In_ PVOID Ptr,
495 _In_ ULONG Quota
496 )
497 {
498 UNREFERENCED_PARAMETER(Quota);
499
500 /* Call the heap allocator */
501 BlMmFreeHeap(Ptr);
502 }
503
504 FORCEINLINE
505 VOID
506 BiDereferenceHive (
507 _In_ HANDLE KeyHandle
508 )
509 {
510 PBI_KEY_OBJECT KeyObject;
511
512 /* Get the key object */
513 KeyObject = (PBI_KEY_OBJECT)KeyHandle;
514
515 /* Drop a reference on the parent hive */
516 --KeyObject->KeyHive->ReferenceCount;
517 }
518
519 VOID
520 BiFlushHive (
521 _In_ HANDLE KeyHandle
522 )
523 {
524 /* Not yet implemented */
525 EfiPrintf(L"NO reg flush\r\n");
526 return;
527 }
528
529 VOID
530 BiCloseKey (
531 _In_ HANDLE KeyHandle
532 )
533 {
534 PBI_KEY_HIVE KeyHive;
535 PBI_KEY_OBJECT KeyObject;
536
537 /* Get the key object and hive */
538 KeyObject = (PBI_KEY_OBJECT)KeyHandle;
539 KeyHive = KeyObject->KeyHive;
540
541 /* Check if we have a hive, or name, or key node */
542 if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
543 {
544 /* Drop a reference, see if it's the last one */
545 BiDereferenceHive(KeyHandle);
546 if (!KeyHive->ReferenceCount)
547 {
548 /* Check if we should flush it */
549 if (KeyHive->Flags & BI_FLUSH_HIVE)
550 {
551 BiFlushHive(KeyHandle);
552 }
553
554 /* Unmap the hive */
555 //MmPapFreePages(KeyHive->ImageBase, 1);
556 EfiPrintf(L"Leaking hive memory\r\n");
557
558 /* Free the hive and hive path */
559 BlMmFreeHeap(KeyHive->FilePath);
560 BlMmFreeHeap(KeyHive);
561 }
562
563 /* Check if a key name is present */
564 if (KeyObject->KeyName)
565 {
566 /* Free it */
567 BlMmFreeHeap(KeyObject->KeyName);
568 }
569 }
570
571 /* Free the object */
572 BlMmFreeHeap(KeyObject);
573 }
574
575 NTSTATUS
576 BiOpenKey(
577 _In_ HANDLE ParentHandle,
578 _In_ PWCHAR KeyName,
579 _Out_ PHANDLE Handle
580 )
581 {
582 PBI_KEY_OBJECT ParentKey, NewKey;
583 PBI_KEY_HIVE ParentHive;
584 NTSTATUS Status;
585 ULONG NameLength, SubNameLength, NameBytes;
586 PWCHAR NameStart, NameBuffer;
587 UNICODE_STRING KeyString;
588 HCELL_INDEX KeyCell;
589 PHHIVE Hive;
590 PCM_KEY_NODE ParentNode;
591
592 /* Convert from a handle to our key object */
593 ParentKey = (PBI_KEY_OBJECT)ParentHandle;
594
595 /* Extract the hive and node information */
596 ParentHive = ParentKey->KeyHive;
597 ParentNode = ParentKey->KeyNode;
598 Hive = &ParentKey->KeyHive->Hive.Hive;
599
600 /* Initialize variables */
601 KeyCell = HCELL_NIL;
602 Status = STATUS_SUCCESS;
603 NameBuffer = NULL;
604
605 /* Loop as long as there's still portions of the key name in play */
606 NameLength = wcslen(KeyName);
607 while (NameLength)
608 {
609 /* Find the first path separator */
610 NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR);
611 if (NameStart)
612 {
613 /* Look only at the key before the separator */
614 SubNameLength = NameStart - KeyName;
615 ++NameStart;
616 }
617 else
618 {
619 /* No path separator, this is the final leaf key */
620 SubNameLength = NameLength;
621 }
622
623 /* Free the name buffer from the previous pass if needed */
624 if (NameBuffer)
625 {
626 BlMmFreeHeap(NameBuffer);
627 }
628
629 /* Allocate a buffer to hold the name of this specific subkey only */
630 NameBytes = SubNameLength * sizeof(WCHAR);
631 NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL));
632 if (!NameBuffer)
633 {
634 Status = STATUS_NO_MEMORY;
635 goto Quickie;
636 }
637
638 /* Copy and null-terminate the name of the subkey */
639 RtlCopyMemory(NameBuffer, KeyName, NameBytes);
640 NameBuffer[SubNameLength] = UNICODE_NULL;
641
642 /* Convert it into a UNICODE_STRING and try to find it */
643 RtlInitUnicodeString(&KeyString, NameBuffer);
644 KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
645 if (KeyCell == HCELL_NIL)
646 {
647 Status = STATUS_OBJECT_NAME_NOT_FOUND;
648 goto Quickie;
649 }
650
651 /* We found it -- get the key node out of it */
652 ParentNode = (PCM_KEY_NODE)HvGetCell(Hive, KeyCell);
653 if (!ParentNode)
654 {
655 Status = STATUS_REGISTRY_CORRUPT;
656 goto Quickie;
657 }
658
659 /* Update the key name to the next remaining path element */
660 KeyName = NameStart;
661 if (NameStart)
662 {
663 /* Update the length to the remainder of the path */
664 NameLength += -1 - SubNameLength;
665 }
666 else
667 {
668 /* There's nothing left, this was the leaf key */
669 NameLength = 0;
670 }
671 }
672
673 /* Allocate a key object */
674 NewKey = BlMmAllocateHeap(sizeof(*NewKey));
675 if (!NewKey)
676 {
677 /* Bail out if we had no memory for it */
678 Status = STATUS_NO_MEMORY;
679 goto Quickie;
680 }
681
682 /* Fill out the key object data */
683 NewKey->KeyNode = ParentNode;
684 NewKey->KeyHive = ParentHive;
685 NewKey->KeyName = NameBuffer;
686 NewKey->KeyCell = KeyCell;
687
688 /* Add a reference to the hive */
689 ++ParentHive->ReferenceCount;
690
691 /* Return the object back to the caller */
692 *Handle = NewKey;
693
694 Quickie:
695 /* If we had a name buffer, free it */
696 if (NameBuffer)
697 {
698 BlMmFreeHeap(NameBuffer);
699 }
700
701 /* Return status of the open operation */
702 return Status;
703 }
704
705 BOOLEAN BiHiveHashLibraryInitialized;
706 ULONGLONG HvSymcryptSeed;
707
708 BOOLEAN
709 HvIsInPlaceBaseBlockValid (
710 _In_ PHBASE_BLOCK BaseBlock
711 )
712 {
713 ULONG HiveLength, HeaderSum;
714 BOOLEAN Valid;
715
716 /* Assume failure */
717 Valid = FALSE;
718
719 /* Check for incorrect signature, type, version, or format */
720 if ((BaseBlock->Signature == 'fger') &&
721 (BaseBlock->Type == 0) &&
722 (BaseBlock->Major <= 1) &&
723 (BaseBlock->Minor <= 5) &&
724 (BaseBlock->Minor >= 3) &&
725 (BaseBlock->Format == 1))
726 {
727 /* Check for invalid hive size */
728 HiveLength = BaseBlock->Length;
729 if (HiveLength)
730 {
731 /* Check for misaligned or too large hive size */
732 if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000)
733 {
734 /* Check for invalid header checksum */
735 HeaderSum = HvpHiveHeaderChecksum(BaseBlock);
736 if (HeaderSum == BaseBlock->CheckSum)
737 {
738 /* All good */
739 Valid = TRUE;
740 }
741 }
742 }
743 }
744
745 /* Return validity */
746 return Valid;
747 }
748
749 NTSTATUS
750 BiInitializeAndValidateHive (
751 _In_ PBI_KEY_HIVE Hive
752 )
753 {
754 ULONG HiveSize;
755 NTSTATUS Status;
756
757 /* Make sure the hive is at least the size of a base block */
758 if (Hive->HiveSize < sizeof(HBASE_BLOCK))
759 {
760 return STATUS_REGISTRY_CORRUPT;
761 }
762
763 /* Make sure that the base block accurately describes the size of the hive */
764 HiveSize = Hive->BaseBlock->Length + sizeof(HBASE_BLOCK);
765 if ((HiveSize < sizeof(HBASE_BLOCK)) || (HiveSize > Hive->HiveSize))
766 {
767 return STATUS_REGISTRY_CORRUPT;
768 }
769
770 /* Initialize a flat memory hive */
771 RtlZeroMemory(&Hive->Hive, sizeof(Hive->Hive));
772 Status = HvInitialize(&Hive->Hive.Hive,
773 HINIT_FLAT,
774 0,
775 0,
776 Hive->BaseBlock,
777 CmpAllocate,
778 CmpFree,
779 NULL,
780 NULL,
781 NULL,
782 NULL,
783 0,
784 NULL);
785 if (NT_SUCCESS(Status))
786 {
787 /* Cleanup volatile/old data */
788 CmPrepareHive(&Hive->Hive.Hive); // CmCheckRegistry
789 Status = STATUS_SUCCESS;
790 }
791
792 /* Return the final status */
793 return Status;
794 }
795
796 NTSTATUS
797 BiLoadHive (
798 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
799 _Out_ PHANDLE HiveHandle
800 )
801 {
802 ULONG DeviceId;
803 PHBASE_BLOCK BaseBlock, NewBaseBlock;
804 PBI_KEY_OBJECT KeyObject;
805 PBI_KEY_HIVE BcdHive;
806 PBL_DEVICE_DESCRIPTOR BcdDevice;
807 ULONG PathLength, DeviceLength, HiveSize, HiveLength, NewHiveSize;
808 PWCHAR HiveName, LogName;
809 BOOLEAN HaveWriteAccess;
810 NTSTATUS Status;
811 PVOID LogData;
812 PHHIVE Hive;
813 UNICODE_STRING KeyString;
814 PCM_KEY_NODE RootNode;
815 HCELL_INDEX CellIndex;
816
817 /* Initialize variables */
818 DeviceId = -1;
819 BaseBlock = NULL;
820 BcdHive = NULL;
821 KeyObject = NULL;
822 LogData = NULL;
823 LogName = NULL;
824
825 /* Initialize the crypto seed */
826 if (!BiHiveHashLibraryInitialized)
827 {
828 HvSymcryptSeed = 0x82EF4D887A4E55C5;
829 BiHiveHashLibraryInitialized = TRUE;
830 }
831
832 /* Extract and validate the input path */
833 BcdDevice = (PBL_DEVICE_DESCRIPTOR)&FilePath->Path;
834 PathLength = FilePath->Length;
835 DeviceLength = BcdDevice->Size;
836 HiveName = (PWCHAR)((ULONG_PTR)BcdDevice + BcdDevice->Size);
837 if (PathLength <= DeviceLength)
838 {
839 /* Doesn't make sense, bail out */
840 Status = STATUS_INVALID_PARAMETER;
841 goto Quickie;
842 }
843
844 /* Attempt to open the underlying device for RW access */
845 HaveWriteAccess = TRUE;
846 Status = BlpDeviceOpen(BcdDevice,
847 BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
848 0,
849 &DeviceId);
850 if (!NT_SUCCESS(Status))
851 {
852 /* Try for RO access instead */
853 HaveWriteAccess = FALSE;
854 Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId);
855 if (!NT_SUCCESS(Status))
856 {
857 /* No access at all -- bail out */
858 goto Quickie;
859 }
860 }
861
862 /* Now try to load the hive on disk */
863 Status = BlImgLoadImageWithProgress2(DeviceId,
864 BlLoaderRegistry,
865 HiveName,
866 (PVOID*)&BaseBlock,
867 &HiveSize,
868 0,
869 FALSE,
870 NULL,
871 NULL);
872 if (!NT_SUCCESS(Status))
873 {
874 EfiPrintf(L"Hive read failure: % lx\r\n", Status);
875 goto Quickie;
876 }
877
878 /* Allocate a hive structure */
879 BcdHive = BlMmAllocateHeap(sizeof(*BcdHive));
880 if (!BcdHive)
881 {
882 Status = STATUS_NO_MEMORY;
883 goto Quickie;
884 }
885
886 /* Initialize it */
887 RtlZeroMemory(BcdHive, sizeof(*BcdHive));
888 BcdHive->BaseBlock = BaseBlock;
889 BcdHive->HiveSize = HiveSize;
890 if (HaveWriteAccess)
891 {
892 BcdHive->Flags |= BI_HIVE_WRITEABLE;
893 }
894
895 /* Make sure the hive was at least one bin long */
896 if (HiveSize < sizeof(*BaseBlock))
897 {
898 Status = STATUS_REGISTRY_CORRUPT;
899 goto Quickie;
900 }
901
902 /* Make sure the hive contents are at least one bin long */
903 HiveLength = BaseBlock->Length;
904 if (BaseBlock->Length < sizeof(*BaseBlock))
905 {
906 Status = STATUS_REGISTRY_CORRUPT;
907 goto Quickie;
908 }
909
910 /* Validate the initial bin (the base block) */
911 if (!HvIsInPlaceBaseBlockValid(BaseBlock))
912 {
913 EfiPrintf(L"Recovery not implemented\r\n");
914 Status = STATUS_REGISTRY_CORRUPT;
915 goto Quickie;
916 }
917
918 /* Check if there's log recovery that needs to happen */
919 if (BaseBlock->Sequence1 != BaseBlock->Sequence2)
920 {
921 EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
922 Status = STATUS_REGISTRY_CORRUPT;
923 goto Quickie;
924 }
925
926 /*
927 * Check if the whole hive doesn't fit in the buffer.
928 * Note: HiveLength does not include the size of the baseblock itself
929 */
930 if (HiveSize < (HiveLength + sizeof(*BaseBlock)))
931 {
932 EfiPrintf(L"Need bigger hive buffer path\r\n");
933
934 /* Allocate a slightly bigger buffer */
935 NewHiveSize = HiveLength + sizeof(*BaseBlock);
936 Status = MmPapAllocatePagesInRange((PVOID*)&NewBaseBlock,
937 BlLoaderRegistry,
938 NewHiveSize >> PAGE_SHIFT,
939 0,
940 0,
941 NULL,
942 0);
943 if (!NT_SUCCESS(Status))
944 {
945 goto Quickie;
946 }
947
948 /* Copy the current data in there */
949 RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize);
950
951 /* Free the old data */
952 EfiPrintf(L"Leaking old hive buffer\r\n");
953 //MmPapFreePages(BaseBlock, 1);
954
955 /* Update our pointers */
956 BaseBlock = NewBaseBlock;
957 HiveSize = NewHiveSize;
958 BcdHive->BaseBlock = BaseBlock;
959 BcdHive->HiveSize = HiveSize;
960 }
961
962 /* Check if any log stuff needs to happen */
963 if (LogData)
964 {
965 EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
966 Status = STATUS_REGISTRY_CORRUPT;
967 goto Quickie;
968 }
969
970 /* Call Hv to setup the hive library */
971 Status = BiInitializeAndValidateHive(BcdHive);
972 if (!NT_SUCCESS(Status))
973 {
974 goto Quickie;
975 }
976
977 /* Now get the root node */
978 Hive = &BcdHive->Hive.Hive;
979 RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell);
980 if (!RootNode)
981 {
982 Status = STATUS_OBJECT_NAME_NOT_FOUND;
983 goto Quickie;
984 }
985
986 /* Find the Objects subkey under it to see if it's a real BCD hive */
987 RtlInitUnicodeString(&KeyString, L"Objects");
988 CellIndex = CmpFindSubKeyByName(Hive, RootNode, &KeyString);
989 if (CellIndex == HCELL_NIL)
990 {
991 EfiPrintf(L"No OBJECTS subkey found!\r\n");
992 Status = STATUS_OBJECT_NAME_NOT_FOUND;
993 goto Quickie;
994 }
995
996 /* This is a valid BCD hive, store its root node here */
997 BcdHive->RootNode = RootNode;
998
999 /* Allocate a copy of the file path */
1000 BcdHive->FilePath = BlMmAllocateHeap(FilePath->Length);
1001 if (!BcdHive->FilePath)
1002 {
1003 Status = STATUS_NO_MEMORY;
1004 goto Quickie;
1005 }
1006
1007 /* Make a copy of it */
1008 RtlCopyMemory(BcdHive->FilePath, FilePath, FilePath->Length);
1009
1010 /* Create a key object to describe the rot */
1011 KeyObject = BlMmAllocateHeap(sizeof(*KeyObject));
1012 if (!KeyObject)
1013 {
1014 Status = STATUS_NO_MEMORY;
1015 goto Quickie;
1016 }
1017
1018 /* Fill out the details */
1019 KeyObject->KeyNode = RootNode;
1020 KeyObject->KeyHive = BcdHive;
1021 KeyObject->KeyName = NULL;
1022 KeyObject->KeyCell = Hive->BaseBlock->RootCell;
1023
1024 /* One reference for the key object, plus one lifetime reference */
1025 BcdHive->ReferenceCount = 2;
1026
1027 /* This is the hive handle */
1028 *HiveHandle = KeyObject;
1029
1030 /* We're all good */
1031 Status = STATUS_SUCCESS;
1032
1033 Quickie:
1034 /* If we had a log name, free it */
1035 if (LogName)
1036 {
1037 BlMmFreeHeap(LogName);
1038 }
1039
1040 /* If we had logging data, free it */
1041 if (LogData)
1042 {
1043 EfiPrintf(L"Leaking log buffer\r\n");
1044 //MmPapFreePages(LogData, 1);
1045 }
1046
1047 /* Check if this is the failure path */
1048 if (!NT_SUCCESS(Status))
1049 {
1050 /* If we mapped the hive, free it */
1051 if (BaseBlock)
1052 {
1053 EfiPrintf(L"Leaking base block on failure\r\n");
1054 //MmPapFreePages(BaseBlock, 1u);
1055 }
1056
1057 /* If we opened the device, close it */
1058 if (DeviceId != -1)
1059 {
1060 BlDeviceClose(DeviceId);
1061 }
1062
1063 /* Did we create a hive object? */
1064 if (BcdHive)
1065 {
1066 /* Free the file path if we made a copy of it */
1067 if (BcdHive->FilePath)
1068 {
1069 BlMmFreeHeap(BcdHive->FilePath);
1070 }
1071
1072 /* Free the hive itself */
1073 BlMmFreeHeap(BcdHive);
1074 }
1075
1076 /* Finally, free the root key object if we created one */
1077 if (KeyObject)
1078 {
1079 BlMmFreeHeap(KeyObject);
1080 }
1081 }
1082
1083 /* Return the final status */
1084 return Status;
1085 }
1086
1087 NTSTATUS
1088 BiAddStoreFromFile (
1089 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
1090 _Out_ PHANDLE StoreHandle
1091 )
1092 {
1093 NTSTATUS Status;
1094 HANDLE HiveHandle, KeyHandle;
1095
1096 /* Load the specified hive */
1097 Status = BiLoadHive(FilePath, &HiveHandle);
1098 if (!NT_SUCCESS(Status))
1099 {
1100 return Status;
1101 }
1102
1103 /* Open the description key to make sure this is really a BCD */
1104 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
1105 if (NT_SUCCESS(Status))
1106 {
1107 /* It is -- close the key as we don't need it */
1108 BiCloseKey(KeyHandle);
1109 *StoreHandle = HiveHandle;
1110 }
1111 else
1112 {
1113 /* Failure, drop a reference on the hive and close the key */
1114 BiDereferenceHive(HiveHandle);
1115 BiCloseKey(HiveHandle);
1116 }
1117
1118 /* Return the status */
1119 return Status;
1120 }
1121
1122 NTSTATUS
1123 BcdOpenStoreFromFile (
1124 _In_ PUNICODE_STRING FileName,
1125 _In_ PHANDLE StoreHandle
1126 )
1127 {
1128 ULONG Length;
1129 PBL_FILE_PATH_DESCRIPTOR FilePath;
1130 NTSTATUS Status;
1131 HANDLE LocalHandle;
1132
1133 /* Assume failure */
1134 LocalHandle = NULL;
1135
1136 /* Allocate a path descriptor */
1137 Length = FileName->Length + sizeof(*FilePath);
1138 FilePath = BlMmAllocateHeap(Length);
1139 if (!FilePath)
1140 {
1141 return STATUS_NO_MEMORY;
1142 }
1143
1144 /* Initialize it */
1145 FilePath->Version = 1;
1146 FilePath->PathType = InternalPath;
1147 FilePath->Length = Length;
1148
1149 /* Copy the name and NULL-terminate it */
1150 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
1151 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
1152
1153 /* Open the BCD */
1154 Status = BiAddStoreFromFile(FilePath, &LocalHandle);
1155 if (NT_SUCCESS(Status))
1156 {
1157 /* Return the handle on success */
1158 *StoreHandle = LocalHandle;
1159 }
1160
1161 /* Free the descriptor and return the status */
1162 BlMmFreeHeap(FilePath);
1163 return Status;
1164 }
1165