d62ff7cd7f8b04be9c5ff0fcf5dc9af5abab072e
[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
456 typedef struct _BI_KEY_HIVE
457 {
458 PVOID ImageBase;
459 PBL_FILE_PATH_DESCRIPTOR FilePath;
460 CMHIVE Hive;
461 LONG ReferenceCount;
462 ULONG Flags;
463 } BI_KEY_HIVE, *PBI_KEY_HIVE;
464
465 typedef struct _BI_KEY_OBJECT
466 {
467 PBI_KEY_HIVE KeyHive;
468 PCM_KEY_NODE KeyNode;
469 HCELL_INDEX KeyCell;
470 PWCHAR KeyName;
471 } BI_KEY_OBJECT, *PBI_KEY_OBJECT;
472
473 PVOID
474 NTAPI
475 CmpAllocate (
476 _In_ SIZE_T Size,
477 _In_ BOOLEAN Paged,
478 _In_ ULONG Tag
479 )
480 {
481 UNREFERENCED_PARAMETER(Paged);
482 UNREFERENCED_PARAMETER(Tag);
483
484 /* Call the heap allocator */
485 return BlMmAllocateHeap(Size);
486 }
487
488 VOID
489 NTAPI
490 CmpFree (
491 _In_ PVOID Ptr,
492 _In_ ULONG Quota
493 )
494 {
495 UNREFERENCED_PARAMETER(Quota);
496
497 /* Call the heap allocator */
498 BlMmFreeHeap(Ptr);
499 }
500
501 FORCEINLINE
502 VOID
503 BiDereferenceHive (
504 _In_ HANDLE KeyHandle
505 )
506 {
507 PBI_KEY_OBJECT KeyObject;
508
509 /* Get the key object */
510 KeyObject = (PBI_KEY_OBJECT)KeyHandle;
511
512 /* Drop a reference on the parent hive */
513 --KeyObject->KeyHive->ReferenceCount;
514 }
515
516 VOID
517 BiFlushHive (
518 _In_ HANDLE KeyHandle
519 )
520 {
521 /* Not yet implemented */
522 EfiPrintf(L"NO reg flush\r\n");
523 return;
524 }
525
526 VOID
527 BiCloseKey (
528 _In_ HANDLE KeyHandle
529 )
530 {
531 PBI_KEY_HIVE KeyHive;
532 PBI_KEY_OBJECT KeyObject;
533
534 /* Get the key object and hive */
535 KeyObject = (PBI_KEY_OBJECT)KeyHandle;
536 KeyHive = KeyObject->KeyHive;
537
538 /* Check if we have a hive, or name, or key node */
539 if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
540 {
541 /* Drop a reference, see if it's the last one */
542 BiDereferenceHive(KeyHandle);
543 if (!KeyHive->ReferenceCount)
544 {
545 /* Check if we should flush it */
546 if (KeyHive->Flags & BI_FLUSH_HIVE)
547 {
548 BiFlushHive(KeyHandle);
549 }
550
551 /* Unmap the hive */
552 //MmPapFreePages(KeyHive->ImageBase, 1);
553 EfiPrintf(L"Leaking hive memory\r\n");
554
555 /* Free the hive and hive path */
556 BlMmFreeHeap(KeyHive->FilePath);
557 BlMmFreeHeap(KeyHive);
558 }
559
560 /* Check if a key name is present */
561 if (KeyObject->KeyName)
562 {
563 /* Free it */
564 BlMmFreeHeap(KeyObject->KeyName);
565 }
566 }
567
568 /* Free the object */
569 BlMmFreeHeap(KeyObject);
570 }
571
572 NTSTATUS
573 BiOpenKey(
574 _In_ HANDLE ParentHandle,
575 _In_ PWCHAR KeyName,
576 _Out_ PHANDLE Handle
577 )
578 {
579 PBI_KEY_OBJECT ParentKey, NewKey;
580 PBI_KEY_HIVE ParentHive;
581 NTSTATUS Status;
582 ULONG NameLength, SubNameLength, NameBytes;
583 PWCHAR NameStart, NameBuffer;
584 UNICODE_STRING KeyString;
585 HCELL_INDEX KeyCell;
586 PHHIVE Hive;
587 PCM_KEY_NODE ParentNode;
588
589 /* Convert from a handle to our key object */
590 ParentKey = (PBI_KEY_OBJECT)ParentHandle;
591
592 /* Extract the hive and node information */
593 ParentHive = ParentKey->KeyHive;
594 ParentNode = ParentKey->KeyNode;
595 Hive = &ParentKey->KeyHive->Hive.Hive;
596
597 /* Initialize variables */
598 KeyCell = HCELL_NIL;
599 Status = STATUS_SUCCESS;
600 NameBuffer = NULL;
601
602 /* Loop as long as there's still portions of the key name in play */
603 NameLength = wcslen(KeyName);
604 while (NameLength)
605 {
606 /* Find the first path separator */
607 NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR);
608 if (NameStart)
609 {
610 /* Look only at the key before the separator */
611 SubNameLength = NameStart - KeyName;
612 ++NameStart;
613 }
614 else
615 {
616 /* No path separator, this is the final leaf key */
617 SubNameLength = NameLength;
618 }
619
620 /* Free the name buffer from the previous pass if needed */
621 if (NameBuffer)
622 {
623 BlMmFreeHeap(NameBuffer);
624 }
625
626 /* Allocate a buffer to hold the name of this specific subkey only */
627 NameBytes = SubNameLength * sizeof(WCHAR);
628 NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL));
629 if (!NameBuffer)
630 {
631 Status = STATUS_NO_MEMORY;
632 goto Quickie;
633 }
634
635 /* Copy and null-terminate the name of the subkey */
636 RtlCopyMemory(NameBuffer, KeyName, NameBytes);
637 NameBuffer[SubNameLength] = UNICODE_NULL;
638
639 /* Convert it into a UNICODE_STRING and try to find it */
640 RtlInitUnicodeString(&KeyString, NameBuffer);
641 KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
642 if (KeyCell == HCELL_NIL)
643 {
644 Status = STATUS_OBJECT_NAME_NOT_FOUND;
645 goto Quickie;
646 }
647
648 /* We found it -- get the key node out of it */
649 ParentNode = (PCM_KEY_NODE)Hive->GetCellRoutine(Hive, KeyCell);
650 if (!ParentNode)
651 {
652 Status = STATUS_REGISTRY_CORRUPT;
653 goto Quickie;
654 }
655
656 /* Update the key name to the next remaining path element */
657 KeyName = NameStart;
658 if (NameStart)
659 {
660 /* Update the length to the remainder of the path */
661 NameLength += -1 - SubNameLength;
662 }
663 else
664 {
665 /* There's nothing left, this was the leaf key */
666 NameLength = 0;
667 }
668 }
669
670 /* Allocate a key object */
671 NewKey = BlMmAllocateHeap(sizeof(*NewKey));
672 if (!NewKey)
673 {
674 /* Bail out if we had no memory for it */
675 Status = STATUS_NO_MEMORY;
676 goto Quickie;
677 }
678
679 /* Fill out the key object data */
680 NewKey->KeyNode = ParentNode;
681 NewKey->KeyHive = ParentHive;
682 NewKey->KeyName = NameBuffer;
683 NewKey->KeyCell = KeyCell;
684
685 /* Add a reference to the hive */
686 ++ParentHive->ReferenceCount;
687
688 /* Return the object back to the caller */
689 *Handle = NewKey;
690
691 Quickie:
692 /* If we had a name buffer, free it */
693 if (NameBuffer)
694 {
695 BlMmFreeHeap(NameBuffer);
696 }
697
698 /* Return status of the open operation */
699 return Status;
700 }
701
702 NTSTATUS
703 BiLoadHive (
704 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
705 _Out_ PHANDLE HiveHandle
706 )
707 {
708 /* This is TODO */
709 EfiPrintf(L"Loading a hive is not yet implemented\r\n");
710 *HiveHandle = NULL;
711 return STATUS_NOT_IMPLEMENTED;
712 }
713
714 NTSTATUS
715 BiAddStoreFromFile (
716 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
717 _Out_ PHANDLE StoreHandle
718 )
719 {
720 NTSTATUS Status;
721 HANDLE HiveHandle, KeyHandle;
722
723 /* Load the specified hive */
724 Status = BiLoadHive(FilePath, &HiveHandle);
725 if (!NT_SUCCESS(Status))
726 {
727 return Status;
728 }
729
730 /* Open the description key to make sure this is really a BCD */
731 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
732 if (NT_SUCCESS(Status))
733 {
734 /* It is -- close the key as we don't need it */
735 BiCloseKey(KeyHandle);
736 *StoreHandle = HiveHandle;
737 }
738 else
739 {
740 /* Failure, drop a reference on the hive and close the key */
741 BiDereferenceHive(HiveHandle);
742 BiCloseKey(HiveHandle);
743 }
744
745 /* Return the status */
746 return Status;
747 }
748
749 NTSTATUS
750 BcdOpenStoreFromFile (
751 _In_ PUNICODE_STRING FileName,
752 _In_ PHANDLE StoreHandle
753 )
754 {
755 ULONG Length;
756 PBL_FILE_PATH_DESCRIPTOR FilePath;
757 NTSTATUS Status;
758 HANDLE LocalHandle;
759
760 /* Assume failure */
761 LocalHandle = NULL;
762 EfiPrintf(L"Opening BCD store: %wZ\n", FileName);
763
764 /* Allocate a path descriptor */
765 Length = FileName->Length + sizeof(*FilePath);
766 FilePath = BlMmAllocateHeap(Length);
767 if (!FilePath)
768 {
769 return STATUS_NO_MEMORY;
770 }
771
772 /* Initialize it */
773 FilePath->Version = 1;
774 FilePath->PathType = InternalPath;
775 FilePath->Length = Length;
776
777 /* Copy the name and NULL-terminate it */
778 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
779 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
780
781 /* Open the BCD */
782 Status = BiAddStoreFromFile(FilePath, &LocalHandle);
783 if (NT_SUCCESS(Status))
784 {
785 /* Return the handle on success */
786 *StoreHandle = LocalHandle;
787 }
788
789 /* Free the descriptor and return the status */
790 BlMmFreeHeap(FilePath);
791 return Status;
792 }
793