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