[BOOTMGFW]: Implement the final boot selection & launch loop.
[reactos.git] / reactos / boot / environ / lib / misc / bcdopt.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/misc/bcdopt.c
5 * PURPOSE: Boot Library BCD Option Parsing 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;
156 BcdElementType ElementType;
157 //PGUID AppIdentifier;
158
159 /* Make sure this is a BCD_STRING */
160 ElementType.PackedValue = Type;
161 if (ElementType.Format != BCD_TYPE_STRING)
162 {
163 return STATUS_INVALID_PARAMETER;
164 }
165
166 /* Return the data */
167 Option = MiscGetBootOption(List, Type);
168 if (Option)
169 {
170 /* Extract the string */
171 String = (PWCHAR)((ULONG_PTR)Option + Option->DataOffset);
172 Status = STATUS_SUCCESS;
173 }
174 else
175 {
176 /* No string is present */
177 String = NULL;
178 Status = STATUS_NOT_FOUND;
179 }
180
181 /* Compute the data size */
182 StringLength = Option->DataSize / sizeof(WCHAR);
183
184 #ifdef _SECURE_BOOT_
185 /* Filter out SecureBoot Options */
186 AppIdentifier = BlGetApplicationIdentifier();
187 Status = BlpBootOptionCallbackString(AppIdentifier, Type, String, StringLength, &String, &StringLength);
188 #else
189 #endif
190
191 /* Make sure we have a valid, non-filtered string */
192 if (NT_SUCCESS(Status))
193 {
194 /* Check if we have space for one more character */
195 Status = RtlULongAdd(StringLength, 1, &StringLength);
196 if (NT_SUCCESS(Status))
197 {
198 /* Check if it's safe to multiply by two */
199 Status = RtlULongMult(StringLength, sizeof(WCHAR), &StringLength);
200 if (NT_SUCCESS(Status))
201 {
202 /* Allocate a copy for the string */
203 StringCopy = BlMmAllocateHeap(StringLength);
204 if (StringCopy)
205 {
206 /* NULL-terminate it */
207 RtlCopyMemory(StringCopy,
208 String,
209 StringLength - sizeof(UNICODE_NULL));
210 StringCopy[StringLength] = UNICODE_NULL;
211 *Value = StringCopy;
212 Status = STATUS_SUCCESS;
213 }
214 else
215 {
216 /* No memory, fail */
217 Status = STATUS_NO_MEMORY;
218 }
219 }
220 }
221 }
222
223 /* All done */
224 return Status;
225 }
226
227 NTSTATUS
228 BlGetBootOptionGuidList (
229 _In_ PBL_BCD_OPTION List,
230 _In_ ULONG Type,
231 _Out_ PGUID *Value,
232 _In_ PULONG Count
233 )
234 {
235 NTSTATUS Status;
236 PBL_BCD_OPTION Option;
237 PGUID GuidCopy, Guid;
238 ULONG GuidCount;
239 BcdElementType ElementType;
240
241 /* Make sure this is a BCD_TYPE_OBJECT_LIST */
242 ElementType.PackedValue = Type;
243 if (ElementType.Format != BCD_TYPE_OBJECT_LIST)
244 {
245 return STATUS_INVALID_PARAMETER;
246 }
247
248 /* Return the data */
249 Option = MiscGetBootOption(List, Type);
250 if (!Option)
251 {
252 /* Set failure if no data exists */
253 Status = STATUS_NOT_FOUND;
254 }
255 else
256 {
257 /* Get the GUIDs and allocate a copy for them */
258 Guid = (PGUID)((ULONG_PTR)Option + Option->DataOffset);
259 GuidCopy = BlMmAllocateHeap(Option->DataSize);
260 if (GuidCopy)
261 {
262 /* Copy the GUIDs */
263 RtlCopyMemory(GuidCopy, Guid, Option->DataSize);
264
265 /* Return the number of GUIDs and the start of the array */
266 GuidCount = Option->DataSize / sizeof(GUID);
267 *Value = GuidCopy;
268 *Count = GuidCount;
269 Status = STATUS_SUCCESS;
270 }
271 else
272 {
273 /* No memory for the copy */
274 Status = STATUS_NO_MEMORY;
275 }
276 }
277
278 /* All good */
279 return Status;
280 }
281
282 NTSTATUS
283 BlGetBootOptionDevice (
284 _In_ PBL_BCD_OPTION List,
285 _In_ ULONG Type,
286 _Out_ PBL_DEVICE_DESCRIPTOR* Value,
287 _In_opt_ PBL_BCD_OPTION* ExtraOptions
288 )
289 {
290 NTSTATUS Status;
291 PBL_BCD_OPTION Option, ListData, ListCopy, SecureListData;
292 PBCD_DEVICE_OPTION BcdDevice;
293 ULONG DeviceSize, ListOffset, ListSize;
294 PBL_DEVICE_DESCRIPTOR DeviceDescriptor, SecureDescriptor;
295 //PGUID AppIdentifier;
296 BcdElementType ElementType;
297
298 /* Make sure this is a BCD_TYPE_DEVICE */
299 ElementType.PackedValue = Type;
300 if (ElementType.Format != BCD_TYPE_DEVICE)
301 {
302 return STATUS_INVALID_PARAMETER;
303 }
304
305 /* Return the data */
306 Option = MiscGetBootOption(List, Type);
307 if (!Option)
308 {
309 /* Set failure if no data exists */
310 Status = STATUS_NOT_FOUND;
311 }
312 else
313 {
314 /* Otherwise, read the size of the BCD device encoded */
315 BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)Option + Option->DataOffset);
316 DeviceSize = BcdDevice->DeviceDescriptor.Size;
317
318 /* Allocate a buffer to copy it into */
319 DeviceDescriptor = BlMmAllocateHeap(DeviceSize);
320 if (!DeviceDescriptor)
321 {
322 return STATUS_NO_MEMORY;
323 }
324
325 /* Copy it into that buffer */
326 RtlCopyMemory(DeviceDescriptor, &BcdDevice->DeviceDescriptor, DeviceSize);
327 Status = STATUS_SUCCESS;
328 }
329
330 /* Check if extra options were requested */
331 if (ExtraOptions)
332 {
333 /* See where they are */
334 ListOffset = Option->ListOffset;
335 if (ListOffset)
336 {
337 /* See how big they are */
338 ListData = (PBL_BCD_OPTION)((ULONG_PTR)Option + ListOffset);
339 ListSize = BlGetBootOptionListSize(ListData);
340
341 /* Allocate a buffer to hold them into */
342 ListCopy = BlMmAllocateHeap(ListSize);
343 if (!ListCopy)
344 {
345 Status = STATUS_NO_MEMORY;
346 goto Quickie;
347 }
348
349 /* Copy them in there */
350 RtlCopyMemory(ListCopy, ListData, ListSize);
351 }
352 }
353
354 #ifdef _SECURE_BOOT_
355 /* Filter out SecureBoot Options */
356 AppIdentifier = BlGetApplicationIdentifier();
357 if (BlpBootOptionCallbacks)
358 {
359 DeviceCallback = BlpBootOptionCallbacks->Device;
360 if (DeviceCallback)
361 {
362 Status = DeviceCallback(BlpBootOptionCallbackCookie,
363 Status,
364 0,
365 AppIdentifier,
366 Type,
367 &SecureDescriptor,
368 PtrOptionData);
369 }
370 }
371 #else
372 /* No secure boot, so the secure descriptors are the standard ones */
373 SecureDescriptor = DeviceDescriptor;
374 SecureListData = ListCopy;
375 #endif
376
377 /* Check if the data was read correctly */
378 if (NT_SUCCESS(Status))
379 {
380 /* Check if we had a new descriptor after filtering */
381 if (SecureDescriptor != DeviceDescriptor)
382 {
383 /* Yep -- if we had an old one, free it */
384 if (DeviceDescriptor)
385 {
386 BlMmFreeHeap(DeviceDescriptor);
387 }
388 }
389
390 /* Check if we had a new list after filtering */
391 if (SecureListData != ListCopy)
392 {
393 /* Yep -- if we had an old list, free it */
394 if (ListCopy)
395 {
396 BlMmFreeHeap(ListCopy);
397 }
398 }
399
400 /* Finally, check if the caller wanted extra options */
401 if (ExtraOptions)
402 {
403 /* Yep -- so pass the caller our copy */
404 *ExtraOptions = ListCopy;
405 ListCopy = NULL;
406 }
407
408 /* Caller always wants data back, so pass them our copy */
409 *Value = DeviceDescriptor;
410 DeviceDescriptor = NULL;
411 }
412
413 Quickie:
414 /* On the failure path, if these buffers are active, we should free them */
415 if (ListCopy)
416 {
417 BlMmFreeHeap(ListCopy);
418 }
419 if (DeviceDescriptor)
420 {
421 BlMmFreeHeap(DeviceDescriptor);
422 }
423
424 /* All done */
425 return Status;
426 }
427
428 NTSTATUS
429 BlGetBootOptionInteger (
430 _In_ PBL_BCD_OPTION List,
431 _In_ ULONG Type,
432 _Out_ PULONGLONG Value
433 )
434 {
435 NTSTATUS Status;
436 PBL_BCD_OPTION Option;
437 //PGUID AppIdentifier;
438 BcdElementType ElementType;
439
440 /* Make sure this is a BCD_TYPE_INTEGER */
441 ElementType.PackedValue = Type;
442 if (ElementType.Format != BCD_TYPE_INTEGER)
443 {
444 return STATUS_INVALID_PARAMETER;
445 }
446
447 /* Return the data */
448 Option = MiscGetBootOption(List, Type);
449 if (Option)
450 {
451 *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
452 }
453
454 #ifdef _SECURE_BOOT_
455 /* Filter out SecureBoot Options */
456 AppIdentifier = BlGetApplicationIdentifier();
457 Status = BlpBootOptionCallbackULongLong(AppIdentifier, Type, Value);
458 #else
459 /* Option found */
460 Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
461 #endif
462 return Status;
463 }
464
465 NTSTATUS
466 BlGetBootOptionBoolean (
467 _In_ PBL_BCD_OPTION List,
468 _In_ ULONG Type,
469 _Out_ PBOOLEAN Value
470 )
471 {
472 NTSTATUS Status;
473 PBL_BCD_OPTION Option;
474 //PGUID AppIdentifier;
475 BcdElementType ElementType;
476
477 /* Make sure this is a BCD_TYPE_BOOLEAN */
478 ElementType.PackedValue = Type;
479 if (ElementType.Format != BCD_TYPE_BOOLEAN)
480 {
481 return STATUS_INVALID_PARAMETER;
482 }
483
484 /* Return the data */
485 Option = MiscGetBootOption(List, Type);
486 if (Option)
487 {
488 *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset);
489 }
490
491 #ifdef _SECURE_BOOT_
492 /* Filter out SecureBoot Options */
493 AppIdentifier = BlGetApplicationIdentifier();
494 Status = BlpBootOptionCallbackBoolean(AppIdentifier, Type, Value);
495 #else
496 /* Option found */
497 Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
498 #endif
499 return Status;
500 }
501
502 NTSTATUS
503 BlpGetBootOptionIntegerList (
504 _In_ PBL_BCD_OPTION List,
505 _In_ ULONG Type,
506 _Out_ PULONGLONG* Value,
507 _Out_ PULONGLONG Count,
508 _In_ BOOLEAN NoCopy
509 )
510 {
511 PBL_BCD_OPTION Option;
512 BcdElementType ElementType;
513 PULONGLONG ValueCopy;
514
515 /* Make sure this is a BCD_TYPE_INTEGER_LIST */
516 ElementType.PackedValue = Type;
517 if (ElementType.Format != BCD_TYPE_INTEGER_LIST)
518 {
519 return STATUS_INVALID_PARAMETER;
520 }
521
522 /* Return the data */
523 Option = MiscGetBootOption(List, Type);
524 if (!Option)
525 {
526 return STATUS_NOT_FOUND;
527 }
528
529 /* Check if a copy should be made of it */
530 if (NoCopy)
531 {
532 /* Nope, return the raw value */
533 *Value = (PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
534 }
535 else
536 {
537 /* Allocate a buffer for the copy */
538 ValueCopy = BlMmAllocateHeap(Option->DataSize);
539 if (!ValueCopy)
540 {
541 return STATUS_NO_MEMORY;
542 }
543
544 /* Copy the data in */
545 RtlCopyMemory(ValueCopy,
546 (PVOID)((ULONG_PTR)Option + Option->DataOffset),
547 Option->DataSize);
548
549 /* Return our copy */
550 *Value = ValueCopy;
551 }
552
553 /* Return count and success */
554 *Count = Option->DataSize / sizeof(ULONGLONG);
555 return STATUS_SUCCESS;
556 }
557
558 NTSTATUS
559 BlCopyBootOptions (
560 _In_ PBL_BCD_OPTION OptionList,
561 _Out_ PBL_BCD_OPTION *CopiedOptions
562 )
563 {
564 NTSTATUS Status;
565 ULONG OptionSize;
566 PBL_BCD_OPTION Options;
567
568 /* Assume no options */
569 Status = STATUS_SUCCESS;
570 *CopiedOptions = NULL;
571
572 /* Get the size of the list and allocate a copy for it */
573 OptionSize = BlGetBootOptionListSize(OptionList);
574 Options = BlMmAllocateHeap(OptionSize);
575 if (!Options)
576 {
577 return STATUS_NO_MEMORY;
578 }
579
580 /* Make the copy and return it to the caller */
581 RtlCopyMemory(Options, OptionList, OptionSize);
582 *CopiedOptions = Options;
583 return Status;
584 }
585
586 NTSTATUS
587 BlAppendBootOptions (
588 _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
589 _In_ PBL_BCD_OPTION Options
590 )
591 {
592 ULONG OptionsSize, CurrentSize;
593 PBL_BCD_OPTION NewOptions, CurrentOptions, NextOption;
594 NTSTATUS Status;
595 ULONG CurrentOffset;
596
597 /* Get the current options */
598 CurrentOptions = AppEntry->BcdData;
599
600 /* Calculate the size of the current, and the appended options */
601 CurrentSize = BlGetBootOptionListSize(CurrentOptions);
602 OptionsSize = BlGetBootOptionListSize(Options);
603
604 /* Allocate a buffer for the concatenated (new) options */
605 NewOptions = BlMmAllocateHeap(CurrentSize + OptionsSize);
606 if (!NewOptions)
607 {
608 return STATUS_NO_MEMORY;
609 }
610
611 /* Copy the old options, and the ones to be added */
612 RtlCopyMemory(NewOptions, CurrentOptions, CurrentSize);
613 RtlCopyMemory(&NewOptions[OptionsSize], Options, OptionsSize);
614
615 /* We made it! */
616 Status = STATUS_SUCCESS;
617
618 /* Scan through to the last option in the list */
619 CurrentOffset = 0;
620 do
621 {
622 NextOption = (PBL_BCD_OPTION)((ULONG_PTR)NewOptions + CurrentOffset);
623 CurrentOffset = NextOption->NextEntryOffset;
624 } while (CurrentOffset);
625
626 /* Every other option now has to have its offset adjusted */
627 do
628 {
629 NextOption->NextEntryOffset += OptionsSize;
630 NextOption = (PBL_BCD_OPTION)((ULONG_PTR)NewOptions + NextOption->NextEntryOffset);
631 } while (NextOption->NextEntryOffset);
632
633 /* If we already had internal options, free them */
634 if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL)
635 {
636 BlMmFreeHeap(AppEntry->BcdData);
637 }
638
639 /* Write the new pointer */
640 AppEntry->BcdData = NewOptions;
641
642 /* Options are now internal, not external */
643 AppEntry->Flags &= ~BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL;
644 AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL;
645 return Status;
646 }
647
648 VOID
649 BlRemoveBootOption (
650 _In_ PBL_BCD_OPTION List,
651 _In_ ULONG Type
652 )
653 {
654 PBL_BCD_OPTION Option;
655
656 /* Keep going until the option is gone */
657 while (1)
658 {
659 /* Get the BCD option */
660 Option = MiscGetBootOption(List, Type);
661 if (!Option)
662 {
663 break;
664 }
665
666 /* Pretend it's empty */
667 Option->Empty = TRUE;
668 }
669 }
670
671 NTSTATUS
672 BlReplaceBootOptions (
673 _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
674 _In_ PBL_BCD_OPTION NewOptions
675 )
676 {
677 NTSTATUS Status;
678 ULONG Size;
679
680 /* Make sure there's something to replace with */
681 if (!NewOptions)
682 {
683 return STATUS_INVALID_PARAMETER;
684 }
685
686 /* Check if we already had allocated internal options */
687 if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL)
688 {
689 /* Free them */
690 BlMmFreeHeap(AppEntry->BcdData);
691 }
692
693 /* Reset option flags */
694 AppEntry->Flags &= ~(BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL |
695 BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL);
696
697 /* Reset the options and set success for now */
698 Status = STATUS_SUCCESS;
699 AppEntry->BcdData = NULL;
700
701 /* Get the size of the new list of options */
702 Size = BlGetBootOptionListSize(NewOptions);
703
704 /* Allocate a copy of the new list */
705 NewOptions = BlMmAllocateHeap(Size);
706 if (!NewOptions)
707 {
708 return STATUS_NO_MEMORY;
709 }
710
711 /* Copy it in */
712 RtlCopyMemory(NewOptions, NewOptions, Size);
713
714 /* Set it as the new set of options and return */
715 AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL;
716 AppEntry->BcdData = NewOptions;
717 return Status;
718 }
719