xref: /reactos/boot/environ/lib/misc/bcdopt.c (revision 9ec85c29)
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
MiscGetBootOption(_In_ PBL_BCD_OPTION List,_In_ ULONG Type)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
BlGetBootOptionListSize(_In_ PBL_BCD_OPTION BcdOption)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
BlGetBootOptionSize(_In_ PBL_BCD_OPTION BcdOption)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
BlGetBootOptionString(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PWCHAR * Value)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
BlGetBootOptionGuid(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PGUID Value)228 BlGetBootOptionGuid (
229     _In_ PBL_BCD_OPTION List,
230     _In_ ULONG Type,
231     _Out_ PGUID Value
232     )
233 {
234     NTSTATUS Status;
235     PBL_BCD_OPTION Option;
236     PGUID Guid;
237     BcdElementType ElementType;
238 
239     /* Make sure this is a BCD_TYPE_OBJECT */
240     ElementType.PackedValue = Type;
241     if (ElementType.Format != BCD_TYPE_OBJECT)
242     {
243         return STATUS_INVALID_PARAMETER;
244     }
245 
246     /* Return the data */
247     Option = MiscGetBootOption(List, Type);
248     if (!Option)
249     {
250         /* Set failure if no data exists */
251         Status = STATUS_NOT_FOUND;
252     }
253     else
254     {
255         /* Copy the GUID */
256         Guid = (PGUID)((ULONG_PTR)Option + Option->DataOffset);
257         RtlCopyMemory(Value, Guid, Option->DataSize);
258         Status = STATUS_SUCCESS;
259     }
260 
261     /* All good */
262     return Status;
263 }
264 
265 NTSTATUS
BlGetBootOptionGuidList(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PGUID * Value,_In_ PULONG Count)266 BlGetBootOptionGuidList (
267     _In_ PBL_BCD_OPTION List,
268     _In_ ULONG Type,
269     _Out_ PGUID *Value,
270     _In_ PULONG Count
271     )
272 {
273     NTSTATUS Status;
274     PBL_BCD_OPTION Option;
275     PGUID GuidCopy, Guid;
276     ULONG GuidCount;
277     BcdElementType ElementType;
278 
279     /* Make sure this is a BCD_TYPE_OBJECT_LIST */
280     ElementType.PackedValue = Type;
281     if (ElementType.Format != BCD_TYPE_OBJECT_LIST)
282     {
283         return STATUS_INVALID_PARAMETER;
284     }
285 
286     /* Return the data */
287     Option = MiscGetBootOption(List, Type);
288     if (!Option)
289     {
290         /* Set failure if no data exists */
291         Status = STATUS_NOT_FOUND;
292     }
293     else
294     {
295         /* Get the GUIDs and allocate a copy for them */
296         Guid = (PGUID)((ULONG_PTR)Option + Option->DataOffset);
297         GuidCopy = BlMmAllocateHeap(Option->DataSize);
298         if (GuidCopy)
299         {
300             /* Copy the GUIDs */
301             RtlCopyMemory(GuidCopy, Guid, Option->DataSize);
302 
303             /* Return the number of GUIDs and the start of the array */
304             GuidCount = Option->DataSize / sizeof(GUID);
305             *Value = GuidCopy;
306             *Count = GuidCount;
307             Status = STATUS_SUCCESS;
308         }
309         else
310         {
311             /* No memory for the copy */
312             Status = STATUS_NO_MEMORY;
313         }
314     }
315 
316     /* All good */
317     return Status;
318 }
319 
320 NTSTATUS
BlGetBootOptionDevice(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PBL_DEVICE_DESCRIPTOR * Value,_In_opt_ PBL_BCD_OPTION * ExtraOptions)321 BlGetBootOptionDevice (
322     _In_ PBL_BCD_OPTION List,
323     _In_ ULONG Type,
324     _Out_ PBL_DEVICE_DESCRIPTOR* Value,
325     _In_opt_ PBL_BCD_OPTION* ExtraOptions
326     )
327 {
328     NTSTATUS Status;
329     PBL_BCD_OPTION Option, ListData, ListCopy, SecureListData;
330     PBCD_DEVICE_OPTION BcdDevice;
331     ULONG DeviceSize, ListOffset, ListSize;
332     PBL_DEVICE_DESCRIPTOR DeviceDescriptor, SecureDescriptor;
333     //PGUID AppIdentifier;
334     BcdElementType ElementType;
335 
336     /* Make sure this is a BCD_TYPE_DEVICE */
337     ElementType.PackedValue = Type;
338     if (ElementType.Format != BCD_TYPE_DEVICE)
339     {
340         return STATUS_INVALID_PARAMETER;
341     }
342 
343     /* Return the data */
344     Option = MiscGetBootOption(List, Type);
345     if (!Option)
346     {
347         /* Set failure if no data exists */
348         Status = STATUS_NOT_FOUND;
349     }
350     else
351     {
352         /* Otherwise, read the size of the BCD device encoded */
353         BcdDevice = (PBCD_DEVICE_OPTION)((ULONG_PTR)Option + Option->DataOffset);
354         DeviceSize = BcdDevice->DeviceDescriptor.Size;
355 
356         /* Allocate a buffer to copy it into */
357         DeviceDescriptor = BlMmAllocateHeap(DeviceSize);
358         if (!DeviceDescriptor)
359         {
360             return STATUS_NO_MEMORY;
361         }
362 
363         /* Copy it into that buffer */
364         RtlCopyMemory(DeviceDescriptor, &BcdDevice->DeviceDescriptor, DeviceSize);
365         Status = STATUS_SUCCESS;
366     }
367 
368     /* Check if extra options were requested */
369     if (ExtraOptions)
370     {
371         /* See where they are */
372         ListOffset = Option->ListOffset;
373         if (ListOffset)
374         {
375             /* See how big they are */
376             ListData = (PBL_BCD_OPTION)((ULONG_PTR)Option + ListOffset);
377             ListSize = BlGetBootOptionListSize(ListData);
378 
379             /* Allocate a buffer to hold them into */
380             ListCopy = BlMmAllocateHeap(ListSize);
381             if (!ListCopy)
382             {
383                 Status = STATUS_NO_MEMORY;
384                 goto Quickie;
385             }
386 
387             /* Copy them in there */
388             RtlCopyMemory(ListCopy, ListData, ListSize);
389         }
390     }
391 
392 #ifdef _SECURE_BOOT_
393     /* Filter out SecureBoot Options */
394     AppIdentifier = BlGetApplicationIdentifier();
395     if (BlpBootOptionCallbacks)
396     {
397         DeviceCallback = BlpBootOptionCallbacks->Device;
398         if (DeviceCallback)
399         {
400             Status = DeviceCallback(BlpBootOptionCallbackCookie,
401                                     Status,
402                                     0,
403                                     AppIdentifier,
404                                     Type,
405                                     &SecureDescriptor,
406                                     PtrOptionData);
407         }
408     }
409 #else
410     /* No secure boot, so the secure descriptors are the standard ones */
411     SecureDescriptor = DeviceDescriptor;
412     SecureListData = ListCopy;
413 #endif
414 
415     /* Check if the data was read correctly */
416     if (NT_SUCCESS(Status))
417     {
418         /* Check if we had a new descriptor after filtering */
419         if (SecureDescriptor != DeviceDescriptor)
420         {
421             /* Yep -- if we had an old one, free it */
422             if (DeviceDescriptor)
423             {
424                 BlMmFreeHeap(DeviceDescriptor);
425             }
426         }
427 
428         /* Check if we had a new list after filtering */
429         if (SecureListData != ListCopy)
430         {
431             /* Yep -- if we had an old list, free it */
432             if (ListCopy)
433             {
434                 BlMmFreeHeap(ListCopy);
435             }
436         }
437 
438         /* Finally, check if the caller wanted extra options */
439         if (ExtraOptions)
440         {
441             /* Yep -- so pass the caller our copy */
442             *ExtraOptions = ListCopy;
443             ListCopy = NULL;
444         }
445 
446         /* Caller always wants data back, so pass them our copy */
447         *Value = DeviceDescriptor;
448         DeviceDescriptor = NULL;
449     }
450 
451 Quickie:
452     /* On the failure path, if these buffers are active, we should free them */
453     if (ListCopy)
454     {
455         BlMmFreeHeap(ListCopy);
456     }
457     if (DeviceDescriptor)
458     {
459         BlMmFreeHeap(DeviceDescriptor);
460     }
461 
462     /* All done */
463     return Status;
464 }
465 
466 NTSTATUS
BlGetBootOptionInteger(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PULONGLONG Value)467 BlGetBootOptionInteger (
468     _In_ PBL_BCD_OPTION List,
469     _In_ ULONG Type,
470     _Out_ PULONGLONG Value
471     )
472 {
473     NTSTATUS Status;
474     PBL_BCD_OPTION Option;
475     //PGUID AppIdentifier;
476     BcdElementType ElementType;
477 
478     /* Make sure this is a BCD_TYPE_INTEGER */
479     ElementType.PackedValue = Type;
480     if (ElementType.Format != BCD_TYPE_INTEGER)
481     {
482         return STATUS_INVALID_PARAMETER;
483     }
484 
485     /* Return the data */
486     Option = MiscGetBootOption(List, Type);
487     if (Option)
488     {
489         *Value = *(PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
490     }
491 
492 #ifdef _SECURE_BOOT_
493     /* Filter out SecureBoot Options */
494     AppIdentifier = BlGetApplicationIdentifier();
495     Status = BlpBootOptionCallbackULongLong(AppIdentifier, Type, Value);
496 #else
497     /* Option found */
498     Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
499 #endif
500     return Status;
501 }
502 
503 NTSTATUS
BlGetBootOptionBoolean(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PBOOLEAN Value)504 BlGetBootOptionBoolean (
505     _In_ PBL_BCD_OPTION List,
506     _In_ ULONG Type,
507     _Out_ PBOOLEAN Value
508     )
509 {
510     NTSTATUS Status;
511     PBL_BCD_OPTION Option;
512     //PGUID AppIdentifier;
513     BcdElementType ElementType;
514 
515     /* Make sure this is a BCD_TYPE_BOOLEAN */
516     ElementType.PackedValue = Type;
517     if (ElementType.Format != BCD_TYPE_BOOLEAN)
518     {
519         return STATUS_INVALID_PARAMETER;
520     }
521 
522     /* Return the data */
523     Option = MiscGetBootOption(List, Type);
524     if (Option)
525     {
526         *Value = *(PBOOLEAN)((ULONG_PTR)Option + Option->DataOffset);
527     }
528 
529 #ifdef _SECURE_BOOT_
530     /* Filter out SecureBoot Options */
531     AppIdentifier = BlGetApplicationIdentifier();
532     Status = BlpBootOptionCallbackBoolean(AppIdentifier, Type, Value);
533 #else
534     /* Option found */
535     Status = Option ? STATUS_SUCCESS : STATUS_NOT_FOUND;
536 #endif
537     return Status;
538 }
539 
540 NTSTATUS
BlpGetBootOptionIntegerList(_In_ PBL_BCD_OPTION List,_In_ ULONG Type,_Out_ PULONGLONG * Value,_Out_ PULONGLONG Count,_In_ BOOLEAN NoCopy)541 BlpGetBootOptionIntegerList (
542     _In_ PBL_BCD_OPTION List,
543     _In_ ULONG Type,
544     _Out_ PULONGLONG* Value,
545     _Out_ PULONGLONG Count,
546     _In_ BOOLEAN NoCopy
547     )
548 {
549     PBL_BCD_OPTION Option;
550     BcdElementType ElementType;
551     PULONGLONG ValueCopy;
552 
553     /* Make sure this is a BCD_TYPE_INTEGER_LIST */
554     ElementType.PackedValue = Type;
555     if (ElementType.Format != BCD_TYPE_INTEGER_LIST)
556     {
557         return STATUS_INVALID_PARAMETER;
558     }
559 
560     /* Return the data */
561     Option = MiscGetBootOption(List, Type);
562     if (!Option)
563     {
564         return STATUS_NOT_FOUND;
565     }
566 
567     /* Check if a copy should be made of it */
568     if (NoCopy)
569     {
570         /* Nope, return the raw value */
571         *Value = (PULONGLONG)((ULONG_PTR)Option + Option->DataOffset);
572     }
573     else
574     {
575         /* Allocate a buffer for the copy */
576         ValueCopy = BlMmAllocateHeap(Option->DataSize);
577         if (!ValueCopy)
578         {
579             return STATUS_NO_MEMORY;
580         }
581 
582         /* Copy the data in */
583         RtlCopyMemory(ValueCopy,
584                       (PVOID)((ULONG_PTR)Option + Option->DataOffset),
585                       Option->DataSize);
586 
587         /* Return our copy */
588         *Value = ValueCopy;
589     }
590 
591     /* Return count and success */
592     *Count = Option->DataSize / sizeof(ULONGLONG);
593     return STATUS_SUCCESS;
594 }
595 
596 NTSTATUS
BlCopyBootOptions(_In_ PBL_BCD_OPTION OptionList,_Out_ PBL_BCD_OPTION * CopiedOptions)597 BlCopyBootOptions (
598     _In_ PBL_BCD_OPTION OptionList,
599     _Out_ PBL_BCD_OPTION *CopiedOptions
600     )
601 {
602     NTSTATUS Status;
603     ULONG OptionSize;
604     PBL_BCD_OPTION Options;
605 
606     /* Assume no options */
607     Status = STATUS_SUCCESS;
608     *CopiedOptions = NULL;
609 
610     /* Get the size of the list and allocate a copy for it */
611     OptionSize = BlGetBootOptionListSize(OptionList);
612     Options = BlMmAllocateHeap(OptionSize);
613     if (!Options)
614     {
615         return STATUS_NO_MEMORY;
616     }
617 
618     /* Make the copy and return it to the caller */
619     RtlCopyMemory(Options, OptionList, OptionSize);
620     *CopiedOptions = Options;
621     return Status;
622 }
623 
624 NTSTATUS
BlAppendBootOptionBoolean(_In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,_In_ ULONG OptionId,_In_ BOOLEAN Value)625 BlAppendBootOptionBoolean (
626     _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
627     _In_ ULONG OptionId,
628     _In_ BOOLEAN Value
629     )
630 {
631     NTSTATUS Status;
632     PBL_BCD_OPTION Option;
633 
634     /* Allocate space for the entry -- remember BOOLEANs are USHORTs in BCD */
635     Option = BlMmAllocateHeap(sizeof(*Option) + sizeof(USHORT));
636     if (!Option)
637     {
638         return STATUS_NO_MEMORY;
639     }
640 
641     /* Initialize it and set the boolean to TRUE */
642     RtlZeroMemory(Option, sizeof(*Option) + sizeof(USHORT));
643     Option->DataSize = sizeof(USHORT);
644     Option->Type = OptionId;
645     Option->DataOffset = sizeof(*Option);
646     *(PBOOLEAN)(Option + 1) = Value;
647 
648     /* Append it */
649     Status = BlAppendBootOptions(AppEntry, Option);
650 
651     /* We're all done, free our initial option */
652     BlMmFreeHeap(Option);
653     return Status;
654 }
655 
656 NTSTATUS
BlAppendBootOptionInteger(_In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,_In_ ULONG OptionId,_In_ ULONGLONG Value)657 BlAppendBootOptionInteger (
658     _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
659     _In_ ULONG OptionId,
660     _In_ ULONGLONG Value
661     )
662 {
663     NTSTATUS Status;
664     PBL_BCD_OPTION Option;
665 
666     /* Allocate space for the entry */
667     Option = BlMmAllocateHeap(sizeof(*Option) + sizeof(Value));
668     if (!Option)
669     {
670         return STATUS_NO_MEMORY;
671     }
672 
673     /* Initialize it and set the integer to the given value */
674     RtlZeroMemory(Option, sizeof(*Option) + sizeof(Value));
675     Option->DataSize = sizeof(Value);
676     Option->Type = OptionId;
677     Option->DataOffset = sizeof(*Option);
678     *(PULONGLONG)(Option + 1) = Value;
679 
680     /* Append it */
681     Status = BlAppendBootOptions(AppEntry, Option);
682 
683     /* We're all done, free our initial option */
684     BlMmFreeHeap(Option);
685     return Status;
686 }
687 
688 NTSTATUS
BlAppendBootOptionString(_In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,_In_ ULONG OptionId,_In_ PWCHAR OptionString)689 BlAppendBootOptionString (
690     _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
691     _In_ ULONG OptionId,
692     _In_ PWCHAR OptionString
693     )
694 {
695     NTSTATUS Status;
696     ULONG StringSize;
697     PBL_BCD_OPTION Option;
698 
699     /* Get the length in bytes */
700     Status = RtlULongLongToULong(wcslen(OptionString) * sizeof(WCHAR),
701                                  &StringSize);
702     if (!NT_SUCCESS(Status))
703     {
704         return Status;
705     }
706 
707     /* Add a NULL-terminator */
708     Status = RtlULongAdd(StringSize, sizeof(UNICODE_NULL), &StringSize);
709     if (!NT_SUCCESS(Status))
710     {
711         return Status;
712     }
713 
714     /* Allocate space for the entry */
715     Option = BlMmAllocateHeap(sizeof(*Option) + StringSize);
716     if (!Option)
717     {
718         return STATUS_NO_MEMORY;
719     }
720 
721     /* Initialize it and copy the string value */
722     RtlZeroMemory(Option, sizeof(*Option) + StringSize);
723     Option->DataSize = StringSize;
724     Option->Type = OptionId;
725     Option->DataOffset = sizeof(*Option);
726     wcsncpy((PWCHAR)Option + 1, OptionString, StringSize / sizeof(WCHAR));
727 
728     /* Append it */
729     Status = BlAppendBootOptions(AppEntry, Option);
730 
731     /* We're all done, free our initial option */
732     BlMmFreeHeap(Option);
733     return Status;
734 }
735 
736 NTSTATUS
BlAppendBootOptions(_In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,_In_ PBL_BCD_OPTION Options)737 BlAppendBootOptions (
738     _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
739     _In_ PBL_BCD_OPTION Options
740     )
741 {
742     ULONG OptionsSize, CurrentSize;
743     PBL_BCD_OPTION NewOptions, CurrentOptions, NextOption;
744     NTSTATUS Status;
745     ULONG CurrentOffset;
746 
747     /* Get the current options */
748     CurrentOptions = AppEntry->BcdData;
749 
750     /* Calculate the size of the current, and the appended options */
751     CurrentSize = BlGetBootOptionListSize(CurrentOptions);
752     OptionsSize = BlGetBootOptionListSize(Options);
753 
754     /* Allocate a buffer for the concatenated (new) options */
755     NewOptions = BlMmAllocateHeap(CurrentSize + OptionsSize);
756     if (!NewOptions)
757     {
758         return STATUS_NO_MEMORY;
759     }
760 
761     /* Copy the old options, and the ones to be added */
762     RtlCopyMemory(NewOptions, CurrentOptions, CurrentSize);
763     RtlCopyMemory((PVOID)((ULONG_PTR)NewOptions + CurrentSize),
764                   Options,
765                   OptionsSize);
766 
767     /* We made it! */
768     Status = STATUS_SUCCESS;
769 
770     /* Scan through to the last option in the list */
771     CurrentOffset = 0;
772     do
773     {
774         NextOption = (PBL_BCD_OPTION)((ULONG_PTR)NewOptions + CurrentOffset);
775         CurrentOffset = NextOption->NextEntryOffset;
776     } while (CurrentOffset);
777 
778     /* Every other option now has to have its offset adjusted */
779     do
780     {
781         NextOption->NextEntryOffset += CurrentSize;
782         NextOption = (PBL_BCD_OPTION)((ULONG_PTR)NewOptions + NextOption->NextEntryOffset);
783     } while (NextOption->NextEntryOffset);
784 
785     /* If we already had internal options, free them */
786     if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL)
787     {
788         BlMmFreeHeap(AppEntry->BcdData);
789     }
790 
791     /* Write the new pointer */
792     AppEntry->BcdData = NewOptions;
793 
794     /* Options are now internal, not external */
795     AppEntry->Flags &= ~BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL;
796     AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL;
797     return Status;
798 }
799 
800 VOID
BlRemoveBootOption(_In_ PBL_BCD_OPTION List,_In_ ULONG Type)801 BlRemoveBootOption (
802     _In_ PBL_BCD_OPTION List,
803     _In_ ULONG Type
804     )
805 {
806     PBL_BCD_OPTION Option;
807 
808     /* Keep going until the option is gone */
809     while (1)
810     {
811         /* Get the BCD option */
812         Option = MiscGetBootOption(List, Type);
813         if (!Option)
814         {
815             break;
816         }
817 
818         /* Pretend it's empty */
819         Option->Empty = TRUE;
820     }
821 }
822 
823 NTSTATUS
BlReplaceBootOptions(_In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,_In_ PBL_BCD_OPTION OldOptions)824 BlReplaceBootOptions (
825     _In_ PBL_LOADED_APPLICATION_ENTRY AppEntry,
826     _In_ PBL_BCD_OPTION OldOptions
827     )
828 {
829     NTSTATUS Status;
830     ULONG OptionSize;
831     PBL_BCD_OPTION NewOptions;
832 
833     /* Make sure there's something to replace with */
834     if (!OldOptions)
835     {
836         return STATUS_INVALID_PARAMETER;
837     }
838 
839     /* Check if we already had allocated internal options */
840     if (AppEntry->Flags & BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL)
841     {
842         /* Free them */
843         BlMmFreeHeap(AppEntry->BcdData);
844     }
845 
846     /* Reset option flags */
847     AppEntry->Flags &= ~(BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL |
848                          BL_APPLICATION_ENTRY_BCD_OPTIONS_EXTERNAL);
849 
850     /* Reset the options and set success for now */
851     Status = STATUS_SUCCESS;
852     AppEntry->BcdData = NULL;
853 
854     /* Get the size of the new list of options */
855     OptionSize = BlGetBootOptionListSize(OldOptions);
856 
857     /* Allocate a copy of the new list */
858     NewOptions = BlMmAllocateHeap(OptionSize);
859     if (!NewOptions)
860     {
861         return STATUS_NO_MEMORY;
862     }
863 
864     /* Copy it in */
865     RtlCopyMemory(NewOptions, OldOptions, OptionSize);
866 
867     /* Set it as the new set of options and return */
868     AppEntry->Flags |= BL_APPLICATION_ENTRY_BCD_OPTIONS_INTERNAL;
869     AppEntry->BcdData = NewOptions;
870     return Status;
871 }
872 
873