People's local builds use PCH by default, rosbuilds do not. A recipe for success.
[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;
24
25 /* No options, bail out */
26 if (!List)
27 {
28 return NULL;
29 }
30
31 /* Loop while we find an option */
32 while (TRUE)
33 {
34 /* Get the next option and see if it matches the type */
35 Option = (PBL_BCD_OPTION)((ULONG_PTR)List + NextOption);
36 if ((Option->Type == Type) && !(Option->Empty))
37 {
38 break;
39 }
40
41 /* Store the offset of the next option */
42 NextOption = Option->NextEntryOffset;
43
44 /* Failed to match. Check for list options */
45 ListOption = Option->ListOffset;
46 if (ListOption)
47 {
48 /* Try to get a match in the associated option */
49 Option = MiscGetBootOption((PBL_BCD_OPTION)((ULONG_PTR)Option +
50 ListOption),
51 Type);
52
53 /* Found one, return it */
54 if (Option)
55 {
56 return Option;
57 }
58 }
59 }
60
61 /* We found the option, return it */
62 return Option;
63 }
64
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 != 0);
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 != 0)
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 != 0)
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 }
171 else
172 {
173 /* No string is present */
174 String = NULL;
175 }
176
177 /* Compute the data size */
178 StringLength = Option->DataSize / sizeof(WCHAR);
179
180 #ifdef _SECURE_BOOT_
181 /* Filter out SecureBoot Options */
182 AppIdentifier = BlGetApplicationIdentifier();
183 Status = BlpBootOptionCallbackString(AppIdentifier, Type, String, StringLength, &String, &StringLength);
184 #else
185 Status = STATUS_SUCCESS;
186 #endif
187
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 else
197 {
198 /* Go ahead */
199 Status = STATUS_SUCCESS;
200 }
201
202 /* No overflow? */
203 if (NT_SUCCESS(Status))
204 {
205 /* Check if it's safe to multiply by two */
206 if ((CopyLength * sizeof(WCHAR)) > 0xFFFFFFFF)
207 {
208 /* Nope */
209 CopyLength = -1;
210 Status = STATUS_INTEGER_OVERFLOW;
211 }
212 else
213 {
214 /* We're good, do the multiplication */
215 Status = STATUS_SUCCESS;
216 CopyLength *= sizeof(WCHAR);
217 }
218
219 /* Allocate a copy for the string */
220 if (NT_SUCCESS(Status))
221 {
222 StringCopy = BlMmAllocateHeap(CopyLength);
223 if (StringCopy)
224 {
225 /* NULL-terminate it */
226 RtlCopyMemory(StringCopy,
227 String,
228 CopyLength - sizeof(UNICODE_NULL));
229 StringCopy[CopyLength] = UNICODE_NULL;
230 *Value = StringCopy;
231 Status = STATUS_SUCCESS;
232 }
233 else
234 {
235 /* No memory, fail */
236 Status = STATUS_NO_MEMORY;
237 }
238 }
239 }
240
241 /* All done */
242 return Status;
243 }
244
245 NTSTATUS
246 BlGetBootOptionDevice (
247 _In_ PBL_BCD_OPTION List,
248 _In_ ULONG Type,
249 _Out_ PBL_DEVICE_DESCRIPTOR* Value,
250 _In_opt_ PBL_BCD_OPTION* ExtraOptions
251 )
252 {
253 NTSTATUS Status;
254 PBL_BCD_OPTION Option, ListData, ListCopy, SecureListData;
255 PBCD_DEVICE_OPTION BcdDevice;
256 ULONG DeviceSize, ListOffset, ListSize;
257 PBL_DEVICE_DESCRIPTOR DeviceDescriptor, SecureDescriptor;
258 //PGUID AppIdentifier;
259
260 /* Make sure this is a BCD_DEVICE */
261 if ((Type & 0xF000000) != 0x1000000)
262 {
263 return STATUS_INVALID_PARAMETER;
264 }
265
266 /* Return the data */
267 Option = MiscGetBootOption(List, Type);
268 if (!Option)
269 {
270 /* Set failure if no data exists */
271 Status = STATUS_NOT_FOUND;
272 }
273 else
274 {
275 /* Otherwise, read the size of the BCD device encoded */
276 BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)Option + Option->DataOffset);
277 DeviceSize = BcdDevice->DeviceDescriptor.Size;
278
279 /* Allocate a buffer to copy it into */
280 DeviceDescriptor = BlMmAllocateHeap(DeviceSize);
281 if (!DeviceDescriptor)
282 {
283 return STATUS_NO_MEMORY;
284 }
285
286 /* Copy it into that buffer */
287 RtlCopyMemory(DeviceDescriptor, &BcdDevice->DeviceDescriptor, DeviceSize);
288 Status = STATUS_SUCCESS;
289 }
290
291 /* Check if extra options were requested */
292 if (ExtraOptions)
293 {
294 /* See where they are */
295 ListOffset = Option->ListOffset;
296 if (ListOffset)
297 {
298 /* See how big they are */
299 ListData = (PBL_BCD_OPTION)((ULONG_PTR)Option + ListOffset);
300 ListSize = BlGetBootOptionListSize(ListData);
301
302 /* Allocate a buffer to hold them into */
303 ListCopy = BlMmAllocateHeap(ListSize);
304 if (!ListCopy)
305 {
306 Status = STATUS_NO_MEMORY;
307 goto Quickie;
308 }
309
310 /* Copy them in there */
311 RtlCopyMemory(ListCopy, ListData, ListSize);
312 }
313 }
314
315 #ifdef _SECURE_BOOT_
316 /* Filter out SecureBoot Options */
317 AppIdentifier = BlGetApplicationIdentifier();
318 if (BlpBootOptionCallbacks)
319 {
320 DeviceCallback = BlpBootOptionCallbacks->Device;
321 if (DeviceCallback)
322 {
323 Status = DeviceCallback(BlpBootOptionCallbackCookie,
324 Status,
325 0,
326 AppIdentifier,
327 Type,
328 &SecureDescriptor,
329 PtrOptionData);
330 }
331 }
332 #else
333 /* No secure boot, so the secure descriptors are the standard ones */
334 Status = STATUS_SUCCESS;
335 SecureDescriptor = DeviceDescriptor;
336 SecureListData = ListCopy;
337 #endif
338
339 /* Check if the data was read correctly */
340 if (NT_SUCCESS(Status))
341 {
342 /* Check if we had a new descriptor after filtering */
343 if (SecureDescriptor != DeviceDescriptor)
344 {
345 /* Yep -- if we had an old one, free it */
346 if (DeviceDescriptor)
347 {
348 BlMmFreeHeap(DeviceDescriptor);
349 }
350 }
351
352 /* Check if we had a new list after filtering */
353 if (SecureListData != ListCopy)
354 {
355 /* Yep -- if we had an old list, free it */
356 if (ListCopy)
357 {
358 BlMmFreeHeap(ListCopy);
359 }
360 }
361
362 /* Finally, check if the caller wanted extra options */
363 if (ExtraOptions)
364 {
365 /* Yep -- so pass the caller our copy */
366 *ExtraOptions = ListCopy;
367 ListCopy = NULL;
368 }
369
370 /* Caller always wants data back, so pass them our copy */
371 *Value = DeviceDescriptor;
372 DeviceDescriptor = NULL;
373 }
374
375 Quickie:
376 /* On the failure path, if these buffers are active, we should free them */
377 if (ListCopy)
378 {
379 BlMmFreeHeap(ListCopy);
380 }
381 if (DeviceDescriptor)
382 {
383 BlMmFreeHeap(DeviceDescriptor);
384 }
385
386 /* All done */
387 return Status;
388 }
389
390 NTSTATUS
391 BlGetBootOptionInteger (
392 _In_ PBL_BCD_OPTION List,
393 _In_ ULONG Type,
394 _Out_ PULONGLONG Value
395 )
396 {
397 NTSTATUS Status;
398 PBL_BCD_OPTION Option;
399 //PGUID AppIdentifier;
400
401 /* Make sure this is a BCD_INTEGER */
402 if ((Type & 0xF000000) != 0x5000000)
403 {
404 return STATUS_INVALID_PARAMETER;
405 }
406
407 /* Return the data */
408 Option = MiscGetBootOption(List, Type);
409 if (Option)
410 {
411 *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
412 }
413
414 #ifdef _SECURE_BOOT_
415 /* Filter out SecureBoot Options */
416 AppIdentifier = BlGetApplicationIdentifier();
417 Status = BlpBootOptionCallbackULongLong(AppIdentifier, Type, Value);
418 #else
419 /* Option found */
420 Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
421 #endif
422 return Status;
423 }
424
425 NTSTATUS
426 BlGetBootOptionBoolean (
427 _In_ PBL_BCD_OPTION List,
428 _In_ ULONG Type,
429 _Out_ PBOOLEAN Value
430 )
431 {
432 NTSTATUS Status;
433 PBL_BCD_OPTION Option;
434 //PGUID AppIdentifier;
435
436 /* Make sure this is a BCD_BOOLEAN */
437 if ((Type & 0xF000000) != 0x6000000)
438 {
439 return STATUS_INVALID_PARAMETER;
440 }
441
442 /* Return the data */
443 Option = MiscGetBootOption(List, Type);
444 if (Option)
445 {
446 *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset);
447 }
448
449 #ifdef _SECURE_BOOT_
450 /* Filter out SecureBoot Options */
451 AppIdentifier = BlGetApplicationIdentifier();
452 Status = BlpBootOptionCallbackBoolean(AppIdentifier, Type, Value);
453 #else
454 /* Option found */
455 Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
456 #endif
457 return Status;
458 }
459
460 #define BI_FLUSH_HIVE 0x01
461
462 typedef struct _BI_KEY_HIVE
463 {
464 PVOID ImageBase;
465 PBL_FILE_PATH_DESCRIPTOR FilePath;
466 CMHIVE Hive;
467 LONG ReferenceCount;
468 ULONG Flags;
469 } BI_KEY_HIVE, *PBI_KEY_HIVE;
470
471 typedef struct _BI_KEY_OBJECT
472 {
473 PBI_KEY_HIVE KeyHive;
474 PCM_KEY_NODE KeyNode;
475 HCELL_INDEX KeyCell;
476 PWCHAR KeyName;
477 } BI_KEY_OBJECT, *PBI_KEY_OBJECT;
478
479 PVOID
480 NTAPI
481 CmpAllocate (
482 _In_ SIZE_T Size,
483 _In_ BOOLEAN Paged,
484 _In_ ULONG Tag
485 )
486 {
487 UNREFERENCED_PARAMETER(Paged);
488 UNREFERENCED_PARAMETER(Tag);
489
490 /* Call the heap allocator */
491 return BlMmAllocateHeap(Size);
492 }
493
494 VOID
495 NTAPI
496 CmpFree (
497 _In_ PVOID Ptr,
498 _In_ ULONG Quota
499 )
500 {
501 UNREFERENCED_PARAMETER(Quota);
502
503 /* Call the heap allocator */
504 BlMmFreeHeap(Ptr);
505 }
506
507 FORCEINLINE
508 VOID
509 BiDereferenceHive (
510 _In_ HANDLE KeyHandle
511 )
512 {
513 PBI_KEY_OBJECT KeyObject;
514
515 /* Get the key object */
516 KeyObject = (PBI_KEY_OBJECT)KeyHandle;
517
518 /* Drop a reference on the parent hive */
519 --KeyObject->KeyHive->ReferenceCount;
520 }
521
522 VOID
523 BiFlushHive (
524 _In_ HANDLE KeyHandle
525 )
526 {
527 /* Not yet implemented */
528 EfiPrintf(L"NO reg flush\r\n");
529 return;
530 }
531
532 VOID
533 BiCloseKey (
534 _In_ HANDLE KeyHandle
535 )
536 {
537 PBI_KEY_HIVE KeyHive;
538 PBI_KEY_OBJECT KeyObject;
539
540 /* Get the key object and hive */
541 KeyObject = (PBI_KEY_OBJECT)KeyHandle;
542 KeyHive = KeyObject->KeyHive;
543
544 /* Check if we have a hive, or name, or key node */
545 if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
546 {
547 /* Drop a reference, see if it's the last one */
548 BiDereferenceHive(KeyHandle);
549 if (!KeyHive->ReferenceCount)
550 {
551 /* Check if we should flush it */
552 if (KeyHive->Flags & BI_FLUSH_HIVE)
553 {
554 BiFlushHive(KeyHandle);
555 }
556
557 /* Unmap the hive */
558 //MmPapFreePages(KeyHive->ImageBase, 1);
559 EfiPrintf(L"Leaking hive memory\r\n");
560
561 /* Free the hive and hive path */
562 BlMmFreeHeap(KeyHive->FilePath);
563 BlMmFreeHeap(KeyHive);
564 }
565
566 /* Check if a key name is present */
567 if (KeyObject->KeyName)
568 {
569 /* Free it */
570 BlMmFreeHeap(KeyObject->KeyName);
571 }
572 }
573
574 /* Free the object */
575 BlMmFreeHeap(KeyObject);
576 }
577
578 NTSTATUS
579 BiOpenKey(
580 _In_ HANDLE ParentHandle,
581 _In_ PWCHAR KeyName,
582 _Out_ PHANDLE Handle
583 )
584 {
585 PBI_KEY_OBJECT ParentKey, NewKey;
586 PBI_KEY_HIVE ParentHive;
587 NTSTATUS Status;
588 ULONG NameLength, SubNameLength, NameBytes;
589 PWCHAR NameStart, NameBuffer;
590 UNICODE_STRING KeyString;
591 HCELL_INDEX KeyCell;
592 PHHIVE Hive;
593 PCM_KEY_NODE ParentNode;
594
595 /* Convert from a handle to our key object */
596 ParentKey = (PBI_KEY_OBJECT)ParentHandle;
597
598 /* Extract the hive and node information */
599 ParentHive = ParentKey->KeyHive;
600 ParentNode = ParentKey->KeyNode;
601 Hive = &ParentKey->KeyHive->Hive.Hive;
602
603 /* Initialize variables */
604 KeyCell = HCELL_NIL;
605 Status = STATUS_SUCCESS;
606 NameBuffer = NULL;
607
608 /* Loop as long as there's still portions of the key name in play */
609 NameLength = wcslen(KeyName);
610 while (NameLength)
611 {
612 /* Find the first path separator */
613 NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR);
614 if (NameStart)
615 {
616 /* Look only at the key before the separator */
617 SubNameLength = NameStart - KeyName;
618 ++NameStart;
619 }
620 else
621 {
622 /* No path separator, this is the final leaf key */
623 SubNameLength = NameLength;
624 }
625
626 /* Free the name buffer from the previous pass if needed */
627 if (NameBuffer)
628 {
629 BlMmFreeHeap(NameBuffer);
630 }
631
632 /* Allocate a buffer to hold the name of this specific subkey only */
633 NameBytes = SubNameLength * sizeof(WCHAR);
634 NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL));
635 if (!NameBuffer)
636 {
637 Status = STATUS_NO_MEMORY;
638 goto Quickie;
639 }
640
641 /* Copy and null-terminate the name of the subkey */
642 RtlCopyMemory(NameBuffer, KeyName, NameBytes);
643 NameBuffer[SubNameLength] = UNICODE_NULL;
644
645 /* Convert it into a UNICODE_STRING and try to find it */
646 RtlInitUnicodeString(&KeyString, NameBuffer);
647 KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
648 if (KeyCell == HCELL_NIL)
649 {
650 Status = STATUS_OBJECT_NAME_NOT_FOUND;
651 goto Quickie;
652 }
653
654 /* We found it -- get the key node out of it */
655 ParentNode = (PCM_KEY_NODE)Hive->GetCellRoutine(Hive, KeyCell);
656 if (!ParentNode)
657 {
658 Status = STATUS_REGISTRY_CORRUPT;
659 goto Quickie;
660 }
661
662 /* Update the key name to the next remaining path element */
663 KeyName = NameStart;
664 if (NameStart)
665 {
666 /* Update the length to the remainder of the path */
667 NameLength += -1 - SubNameLength;
668 }
669 else
670 {
671 /* There's nothing left, this was the leaf key */
672 NameLength = 0;
673 }
674 }
675
676 /* Allocate a key object */
677 NewKey = BlMmAllocateHeap(sizeof(*NewKey));
678 if (!NewKey)
679 {
680 /* Bail out if we had no memory for it */
681 Status = STATUS_NO_MEMORY;
682 goto Quickie;
683 }
684
685 /* Fill out the key object data */
686 NewKey->KeyNode = ParentNode;
687 NewKey->KeyHive = ParentHive;
688 NewKey->KeyName = NameBuffer;
689 NewKey->KeyCell = KeyCell;
690
691 /* Add a reference to the hive */
692 ++ParentHive->ReferenceCount;
693
694 /* Return the object back to the caller */
695 *Handle = NewKey;
696
697 Quickie:
698 /* If we had a name buffer, free it */
699 if (NameBuffer)
700 {
701 BlMmFreeHeap(NameBuffer);
702 }
703
704 /* Return status of the open operation */
705 return Status;
706 }
707
708 NTSTATUS
709 BiLoadHive (
710 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
711 _Out_ PHANDLE HiveHandle
712 )
713 {
714 /* This is TODO */
715 EfiPrintf(L"Loading a hive is not yet implemented\r\n");
716 *HiveHandle = NULL;
717 return STATUS_NOT_IMPLEMENTED;
718 }
719
720 NTSTATUS
721 BiAddStoreFromFile (
722 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
723 _Out_ PHANDLE StoreHandle
724 )
725 {
726 NTSTATUS Status;
727 HANDLE HiveHandle, KeyHandle;
728
729 /* Load the specified hive */
730 Status = BiLoadHive(FilePath, &HiveHandle);
731 if (!NT_SUCCESS(Status))
732 {
733 return Status;
734 }
735
736 /* Open the description key to make sure this is really a BCD */
737 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
738 if (NT_SUCCESS(Status))
739 {
740 /* It is -- close the key as we don't need it */
741 BiCloseKey(KeyHandle);
742 *StoreHandle = HiveHandle;
743 }
744 else
745 {
746 /* Failure, drop a reference on the hive and close the key */
747 BiDereferenceHive(HiveHandle);
748 BiCloseKey(HiveHandle);
749 }
750
751 /* Return the status */
752 return Status;
753 }
754
755 NTSTATUS
756 BcdOpenStoreFromFile (
757 _In_ PUNICODE_STRING FileName,
758 _In_ PHANDLE StoreHandle
759 )
760 {
761 ULONG Length;
762 PBL_FILE_PATH_DESCRIPTOR FilePath;
763 NTSTATUS Status;
764 HANDLE LocalHandle;
765
766 /* Assume failure */
767 LocalHandle = NULL;
768
769 /* Allocate a path descriptor */
770 Length = FileName->Length + sizeof(*FilePath);
771 FilePath = BlMmAllocateHeap(Length);
772 if (!FilePath)
773 {
774 return STATUS_NO_MEMORY;
775 }
776
777 /* Initialize it */
778 FilePath->Version = 1;
779 FilePath->PathType = InternalPath;
780 FilePath->Length = Length;
781
782 /* Copy the name and NULL-terminate it */
783 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
784 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
785
786 /* Open the BCD */
787 Status = BiAddStoreFromFile(FilePath, &LocalHandle);
788 if (NT_SUCCESS(Status))
789 {
790 /* Return the handle on success */
791 *StoreHandle = LocalHandle;
792 }
793
794 /* Free the descriptor and return the status */
795 BlMmFreeHeap(FilePath);
796 return Status;
797 }
798