OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
LoaderEntry.c
Go to the documentation of this file.
1
8#include "LinuxBootInternal.h"
9
10#include <Uefi.h>
11#include <Library/BaseLib.h>
12#include <Library/DevicePathLib.h>
13#include <Library/MemoryAllocationLib.h>
16#include <Library/OcFileLib.h>
18#include <Library/OcStringLib.h>
19#include <Library/PrintLib.h>
20#include <Library/SortLib.h>
21#include <Library/UefiBootServicesTableLib.h>
22
24
25//
26// Vars which require blank values if not present after GRUB2+blscfg var parsing, in order
27// to fix up the fact that TuneD does not always initialise them.
28//
29STATIC CHAR8 *mTuneDVars[] = {
30 "tuned_params",
31 "tuned_initrd"
32};
33
34//
35// Root.
36//
37#define ROOT_DIR L"\\"
38//
39// Required where the BTRFS subvolume is /boot, as this looks like a
40// normal directory within EFI. Note that scanning / and then /boot
41// is how blscfg behaves by default too.
42//
43#define BOOT_DIR L"\\boot"
44//
45// Don't add missing root= option unless this directory is present at root.
46//
47#define OSTREE_DIR L"\\ostree"
48
49//
50// No leading slash so they can be relative to root or
51// additional scan dir.
52//
53#define LOADER_ENTRIES_DIR L"loader\\entries"
54#define GRUB2_GRUB_CFG L"grub2\\grub.cfg"
55#define GRUB2_GRUBENV L"grub2\\grubenv"
56#define GRUB2_GRUBENV_SIZE SIZE_1KB
57
58#define BLSPEC_SUFFIX_CONF L".conf"
59#define BLSPEC_PREFIX_AUTO L"auto-"
60
61//
62// Put a limit on entry name length, since the base part of the filename
63// is used for the id, i.e. may be stored in BOOT#### entry in NVRAM.
64// We then re-use the same filename length limit for vmlinuz and initrd files.
65// Since we are using pure EFISTUB loading, initrd file paths have to be passed
66// in kernel args (which have an unknown max. length of 256-4096 bytes).
67//
68#define MAX_LOADER_ENTRY_NAME_LEN (127)
69#define MAX_LOADER_ENTRY_FILE_INFO_SIZE ( \
70 SIZE_OF_EFI_FILE_INFO + \
71 (MAX_LOADER_ENTRY_NAME_LEN + L_STR_LEN (BLSPEC_SUFFIX_CONF) + 1) * sizeof (CHAR16) \
72 )
73
74//
75// Might as well put an upper limit for some kind of sanity check.
76// Typical files are ~350 bytes.
77//
78#define MAX_LOADER_ENTRY_FILE_SIZE SIZE_4KB
79
80//
81// grub2/grub.cfg was found, we treat this as enough to show that /loader/entries
82// we have found are a GRUB2+blscfg setup.
83//
84STATIC
85BOOLEAN
87
88/*
89 loader entry processing states
90*/
98
99//
100// First match, therefore Ubuntu should come after similar variants,
101// and probably very short strings should come last in case they
102// occur elsewhere in another kernel version string.
103// Note: Should be kept in sync with Flavours.md.
104//
105STATIC
106CHAR8 *
108 "Arch",
109 "Astra",
110 "CentOS",
111 "Debian",
112 "Deepin",
113 "elementaryOS",
114 "Endless",
115 "Gentoo",
116 "Fedora",
117 "KDEneon",
118 "Kali",
119 "Mageia",
120 "Manjaro",
121 "Mint",
122 "openSUSE",
123 "Oracle",
124 "PopOS",
125 "RHEL",
126 "Rocky",
127 "Solus",
128 "Lubuntu",
129 "UbuntuMATE",
130 "Xubuntu",
131 "Ubuntu",
132 "Void",
133 "Zorin",
134 "MX"
135};
136
137STATIC
138CHAR8 *
140 IN CHAR8 *String
141 )
142{
143 UINTN Index;
144 CHAR8 *Variant;
145
146 Variant = NULL;
147 for (Index = 0; Index < ARRAY_SIZE (mLinuxVariants); Index++) {
148 if (OcAsciiStriStr (String, mLinuxVariants[Index]) != NULL) {
149 Variant = mLinuxVariants[Index];
150 break;
151 }
152 }
153
154 return Variant;
155}
156
157//
158// Skips comment lines, and lines with key but no value
159// EFI_LOAD_ERROR - File has invalid chars
160//
161STATIC
162EFI_STATUS
164 IN CONST CHAR16 *FileName,
165 IN OUT CHAR8 *Content,
166 IN OUT UINTN *Pos,
167 OUT CHAR8 **Key,
168 OUT CHAR8 **Value
169 )
170{
171 ENTRY_PARSE_STATE State;
172 CHAR8 *LastSpace;
173 CHAR8 Ch;
174 BOOLEAN IsComplete;
175
176 *Key = NULL;
177 *Value = NULL;
178 State = ENTRY_LEADING_SPACE;
179 IsComplete = FALSE;
180
181 do {
182 Ch = Content[*Pos];
183
184 if (!((Ch == '\0') || (Ch == '\t') || (Ch == '\n') || ((Ch >= 20) && (Ch < 128)))) {
185 DEBUG ((DEBUG_WARN, "LNX: Invalid char 0x%x in %s\n", Ch, FileName));
186 return EFI_INVALID_PARAMETER;
187 }
188
189 switch (State) {
191 if ((Ch == '\n') || (Ch == '\0')) {
192 //
193 // Skip empty line
194 //
195 } else if ((Ch == ' ') || (Ch == '\t')) {
196 } else if (Ch == '#') {
197 State = ENTRY_COMMENT;
198 } else {
199 *Key = &Content[*Pos];
200 State = ENTRY_KEY;
201 }
202
203 break;
204
205 case ENTRY_COMMENT:
206 if (Ch == '\n') {
207 State = ENTRY_LEADING_SPACE;
208 }
209
210 break;
211
212 case ENTRY_KEY:
213 if ((Ch == '\n') || (Ch == '\0')) {
214 //
215 // No value, skip line
216 //
217 } else if ((Ch == ' ') || (Ch == '\t')) {
218 Content[*Pos] = '\0';
219 State = ENTRY_KEY_VALUE_SPACE;
220 }
221
222 break;
223
225 if ((Ch == '\n') || (Ch == '\0')) {
226 //
227 // No value, skip line
228 //
229 } else if ((Ch == ' ') || (Ch == '\t')) {
230 } else {
231 *Value = &Content[*Pos];
232 State = ENTRY_VALUE;
233 LastSpace = NULL;
234 }
235
236 break;
237
238 case ENTRY_VALUE:
239 if ((Ch == '\n') || (Ch == '\0')) {
240 if (LastSpace != NULL) {
241 *LastSpace = '\0';
242 } else {
243 Content[*Pos] = '\0';
244 }
245
246 IsComplete = TRUE;
247 } else if ((Ch == ' ') || (Ch == '\t')) {
248 LastSpace = &Content[*Pos];
249 } else {
250 LastSpace = NULL;
251 }
252
253 break;
254
255 default:
256 ASSERT (FALSE);
257 return EFI_INVALID_PARAMETER;
258 }
259
260 if (Ch != '\0') {
261 ++(*Pos);
262 }
263 } while (Ch != '\0' && !IsComplete);
264
265 if (!IsComplete) {
266 return EFI_NOT_FOUND;
267 }
268
269 return EFI_SUCCESS;
270}
271
274 VOID
275 )
276{
277 LOADER_ENTRY *Entry;
278
280
281 if (Entry != NULL) {
282 Entry->Options = OcFlexArrayInit (sizeof (CHAR8 *), OcFlexArrayFreePointerItem);
283 if (Entry->Options == NULL) {
285 Entry = NULL;
286 }
287 }
288
289 if (Entry != NULL) {
290 Entry->Initrds = OcFlexArrayInit (sizeof (CHAR8 *), OcFlexArrayFreePointerItem);
291 if (Entry->Initrds == NULL) {
293 Entry = NULL;
294 }
295 }
296
297 return Entry;
298}
299
300VOID
302 LOADER_ENTRY *Entry
303 )
304{
305 ASSERT (Entry != NULL);
306
307 if (Entry == NULL) {
308 return;
309 }
310
311 if (Entry->Title) {
312 FreePool (Entry->Title);
313 }
314
315 if (Entry->Version) {
316 FreePool (Entry->Version);
317 }
318
319 if (Entry->Linux) {
320 FreePool (Entry->Linux);
321 }
322
323 OcFlexArrayFree (&Entry->Initrds);
324 OcFlexArrayFree (&Entry->Options);
325 if (Entry->OcId != NULL) {
326 FreePool (Entry->OcId);
327 }
328
329 if (Entry->OcFlavour != NULL) {
330 FreePool (Entry->OcFlavour);
331 }
332}
333
334STATIC
335EFI_STATUS
337 IN CONST BOOLEAN Grub2,
338 IN OUT CHAR8 **Target,
339 IN CONST CHAR8 *Value
340 )
341{
342 if (!Grub2 || (*Target == NULL)) {
343 if (*Target != NULL) {
344 FreePool (*Target);
345 }
346
347 *Target = AllocateCopyPool (AsciiStrSize (Value), Value);
348 if (*Target == NULL) {
349 return EFI_OUT_OF_RESOURCES;
350 }
351 }
352
353 return EFI_SUCCESS;
354}
355
356STATIC
357EFI_STATUS
359 IN CONST BOOLEAN Grub2,
360 IN OC_FLEX_ARRAY *Array,
361 IN CONST CHAR8 *Value
362 )
363{
364 VOID **NewItem;
365
366 if (Grub2 && (Array->Items != NULL)) {
367 return EFI_SUCCESS;
368 }
369
370 NewItem = OcFlexArrayAddItem (Array);
371 if (NewItem == NULL) {
372 return EFI_OUT_OF_RESOURCES;
373 }
374
375 *NewItem = AllocateCopyPool (AsciiStrSize (Value), Value);
376 if (*NewItem == NULL) {
377 return EFI_OUT_OF_RESOURCES;
378 }
379
380 return EFI_SUCCESS;
381}
382
383EFI_STATUS
385 IN CONST CHAR16 *FileName,
386 IN OUT CHAR8 *Content,
387 OUT LOADER_ENTRY *Entry,
388 IN CONST BOOLEAN Grub2
389 )
390{
391 EFI_STATUS Status;
392 UINTN Pos;
393 CHAR8 *Key;
394 CHAR8 *Value;
395
396 ASSERT (Content != NULL);
397
398 Status = EFI_SUCCESS;
399 Pos = 0;
400
401 //
402 // Grub2 blscfg module uses first only (even for options,
403 // which should allow more than one according to BL Spec).
404 // systemd-boot uses multiple (concatenated with a space)
405 // for options, and last only for everything which should
406 // have only one.
407 // Both use multiple for initrd.
408 //
409 while (TRUE) {
410 Status = GetLoaderEntryLine (FileName, Content, &Pos, &Key, &Value);
411 if (Status == EFI_NOT_FOUND) {
412 break;
413 }
414
415 if (EFI_ERROR (Status)) {
416 return Status;
417 }
418
419 if (AsciiStrCmp (Key, "title") == 0) {
420 Status = EntryCopySingleValue (Grub2, &Entry->Title, Value);
421 } else if (AsciiStrCmp (Key, "version") == 0) {
422 Status = EntryCopySingleValue (Grub2, &Entry->Version, Value);
423 } else if (AsciiStrCmp (Key, "linux") == 0) {
424 Status = EntryCopySingleValue (Grub2, &Entry->Linux, Value);
425 } else if (AsciiStrCmp (Key, "options") == 0) {
426 Status = EntryCopyMultipleValue (Grub2, Entry->Options, Value);
427 } else if (AsciiStrCmp (Key, "initrd") == 0) {
428 Status = EntryCopyMultipleValue (FALSE, Entry->Initrds, Value);
429 }
430
431 if (EFI_ERROR (Status)) {
432 return Status;
433 }
434 }
435
436 return EFI_SUCCESS;
437}
438
439STATIC
440EFI_STATUS
442 IN OUT LOADER_ENTRY *Entry
443 )
444{
445 EFI_STATUS Status;
446 CHAR8 **Options;
447 CHAR8 *NewOptions;
448 GRUB_VAR *DefaultOptionsVar;
449
450 if (Entry->Options->Count > 0) {
451 Status = InternalExpandGrubVarsForArray (Entry->Options);
452 if (EFI_ERROR (Status)) {
453 return Status;
454 }
455 } else {
456 //
457 // This is what grub2 blscfg does if there are no options.
458 //
459 DefaultOptionsVar = InternalGetGrubVar ("default_kernelopts");
460 if (DefaultOptionsVar != NULL) {
461 if (DefaultOptionsVar->Errors != 0) {
462 DEBUG ((DEBUG_WARN, "LNX: Unusable grub var $%a - 0x%x\n", "default_kernelopts", DefaultOptionsVar->Errors));
463 return EFI_INVALID_PARAMETER;
464 }
465
466 DEBUG ((DEBUG_INFO, "LNX: Using $%a\n", "default_kernelopts"));
467
468 //
469 // Blscfg expands $default_kernelopts, and we expand it even
470 // if !HasVars since we need the string to be realloced.
471 //
472 Status = InternalExpandGrubVars (DefaultOptionsVar->Value, &NewOptions);
473 if (EFI_ERROR (Status)) {
474 return Status;
475 }
476
477 Options = OcFlexArrayAddItem (Entry->Options);
478 if (Options == NULL) {
479 FreePool (NewOptions);
480 return EFI_OUT_OF_RESOURCES;
481 }
482
483 *Options = NewOptions;
484 }
485 }
486
487 return EFI_SUCCESS;
488}
489
490//
491// Expand grub vars, and expand multiple initrds per line to multiple initrd lines.
492// Do this before checking for files, etc.
493//
494STATIC
495EFI_STATUS
497 IN OUT LOADER_ENTRY *Entry
498 )
499{
500 EFI_STATUS Status;
501 UINTN OptionsIndex;
502 CHAR8 **Options;
503 OC_FLEX_ARRAY *ExpandedInitrds;
504 OC_FLEX_ARRAY *SplitInitrds;
505 UINTN SplitInitrdsIndex;
506
507 if (Entry->Initrds->Count == 0) {
508 return EFI_SUCCESS;
509 }
510
511 Status = InternalExpandGrubVarsForArray (Entry->Initrds);
512 if (EFI_ERROR (Status)) {
513 return Status;
514 }
515
516 ExpandedInitrds = OcFlexArrayInit (sizeof (CHAR8 *), OcFlexArrayFreePointerItem);
517
518 for (OptionsIndex = 0; OptionsIndex < Entry->Initrds->Count; OptionsIndex++) {
519 Options = OcFlexArrayItemAt (Entry->Initrds, OptionsIndex);
520
521 Status = OcParseVars (*Options, &SplitInitrds, OcStringFormatAscii, TRUE);
522 if (EFI_ERROR (Status)) {
523 break;
524 }
525
526 for (SplitInitrdsIndex = 0; SplitInitrdsIndex < SplitInitrds->Count; SplitInitrdsIndex++) {
527 Status = EntryCopyMultipleValue (FALSE, ExpandedInitrds, OcParsedVarsItemAt (SplitInitrds, SplitInitrdsIndex)->Ascii.Value);
528 if (EFI_ERROR (Status)) {
529 break;
530 }
531 }
532
533 OcFlexArrayFree (&SplitInitrds);
534 if (EFI_ERROR (Status)) {
535 break;
536 }
537 }
538
539 if (EFI_ERROR (Status)) {
540 OcFlexArrayFree (&ExpandedInitrds);
541 return Status;
542 }
543
544 ASSERT (ExpandedInitrds->Count >= Entry->Initrds->Count);
545
546 OcFlexArrayFree (&Entry->Initrds);
547 Entry->Initrds = ExpandedInitrds;
548
549 return EFI_SUCCESS;
550}
551
552STATIC
553EFI_STATUS
555 EFI_FILE_HANDLE Directory,
556 EFI_FILE_INFO *FileInfo,
557 UINTN FileInfoSize,
558 VOID *Context OPTIONAL
559 )
560{
561 if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
562 return EFI_NOT_FOUND;
563 }
564
565 //
566 // Skip ".*" and case sensitive "auto-*" files,
567 // follows systemd-boot logic.
568 //
569 if ((FileInfo->FileName[0] == L'.') ||
570 (StrnCmp (FileInfo->FileName, BLSPEC_PREFIX_AUTO, L_STR_LEN (BLSPEC_PREFIX_AUTO)) == 0))
571 {
572 return EFI_NOT_FOUND;
573 }
574
575 if (!OcUnicodeEndsWith (FileInfo->FileName, BLSPEC_SUFFIX_CONF, TRUE)) {
576 return EFI_NOT_FOUND;
577 }
578
579 return EFI_SUCCESS;
580}
581
582//
583// Works for vmlinuz-{version} and {machine-id}-{version}.conf.
584// If blspec/blscfg style then we want .conf files with the same machine-id
585// to count as alternate kernels of the same install; but there might be
586// different installs, and the same kernel might be on different installs;
587// so {machine-id} is good as the shared id (and it would not be sufficient
588// to just use {version} when generating a non-shared id).
589// If autodetect, we assume there is only one install in the directory,
590// so {vmlinuz} is good as the shared id.
591//
592EFI_STATUS
594 IN OUT LOADER_ENTRY *Entry,
595 IN CHAR16 *FileName
596 )
597{
598 EFI_STATUS Status;
599 UINTN NumCopied;
600
601 CHAR16 *Split;
602 CHAR16 *IdEnd;
603 CHAR16 *VersionStart;
604 CHAR16 *VersionEnd;
605 CHAR8 *OcId;
606 CHAR8 *Version;
607
608 ASSERT (Entry != NULL);
609 ASSERT (FileName != NULL);
610
611 Split = NULL;
612
613 Split = OcStrChr (FileName, L'-');
614
615 VersionEnd = &FileName[StrLen (FileName)];
616 if (OcUnicodeEndsWith (FileName, BLSPEC_SUFFIX_CONF, TRUE)) {
617 VersionEnd -= L_STR_LEN (BLSPEC_SUFFIX_CONF);
618 }
619
620 //
621 // Will not happen with sane filenames.
622 //
623 if ((Split != NULL) && ((Split == FileName) || (VersionEnd == Split + 1))) {
624 Split = NULL;
625 }
626
627 if (Split == NULL) {
628 VersionStart = FileName;
629 IdEnd = VersionEnd;
630 } else {
631 VersionStart = Split + 1;
632
634 IdEnd = Split;
635 } else {
636 IdEnd = VersionEnd;
637 }
638 }
639
640 OcId = AllocatePool (IdEnd - FileName + 1);
641 if (OcId == NULL) {
642 return EFI_OUT_OF_RESOURCES;
643 }
644
645 Status = UnicodeStrnToAsciiStrS (FileName, IdEnd - FileName, OcId, IdEnd - FileName + 1, &NumCopied);
646 ASSERT_EFI_ERROR (Status);
647 ASSERT (NumCopied == (UINTN)(IdEnd - FileName));
648
649 Entry->OcId = OcId;
650
651 if (Entry->Version == NULL) {
652 Version = AllocatePool (VersionEnd - VersionStart + 1);
653 if (Version == NULL) {
654 return EFI_OUT_OF_RESOURCES;
655 }
656
657 Status = UnicodeStrnToAsciiStrS (VersionStart, VersionEnd - VersionStart, Version, VersionEnd - VersionStart + 1, &NumCopied);
658 ASSERT_EFI_ERROR (Status);
659 ASSERT (NumCopied == (UINTN)(VersionEnd - VersionStart));
660
661 Entry->Version = Version;
662 }
663
664 return EFI_SUCCESS;
665}
666
667//
668// We do not need to warn about missing files: OC warns about
669// missing bootable file (vmlinuz) and kernel stops and warns
670// about missing initrd. However, some linuxes (e.g. Endless)
671// make the filepaths specified in an entry relative to /boot,
672// not relative to / as it should be, so we have to check which
673// it is. Only for this reason, we end up warning here if we
674// can't find it at all.
675//
676STATIC
677EFI_STATUS
679 IN CONST EFI_FILE_HANDLE Directory,
680 IN CONST CHAR16 *DirName,
681 IN OUT CHAR8 **FileName
682 )
683{
684 EFI_STATUS Status;
685 EFI_FILE_PROTOCOL *File;
686 UINTN FileNameLen;
687 UINTN DirNameLen;
688 CHAR16 *Path;
689 UINTN MaxPathSize;
690
691 ASSERT (DirName != NULL);
692 ASSERT (FileName != NULL);
693 ASSERT (*FileName != NULL);
694
695 FileNameLen = AsciiStrLen (*FileName);
696 DirNameLen = StrLen (DirName);
697
698 MaxPathSize = (DirNameLen + FileNameLen + 1) * sizeof (CHAR16);
699
700 Path = AllocatePool (MaxPathSize);
701 if (Path == NULL) {
702 return EFI_OUT_OF_RESOURCES;
703 }
704
705 UnicodeSPrintAsciiFormat (Path, MaxPathSize, "%a", *FileName);
706 UnicodeUefiSlashes (Path);
707
708 Status = OcSafeFileOpen (Directory, &File, Path, EFI_FILE_MODE_READ, 0);
709 if (!EFI_ERROR (Status)) {
710 File->Close (File);
711 FreePool (Path);
712 return Status;
713 }
714
715 if (DirNameLen <= 1) {
716 DEBUG ((DEBUG_WARN, "LNX: %a not found - %r\n", *FileName, Status));
717 FreePool (Path);
718 return Status;
719 }
720
721 UnicodeSPrintAsciiFormat (Path, MaxPathSize, "%s%a", DirName, *FileName);
722 UnicodeUefiSlashes (Path);
723
724 Status = OcSafeFileOpen (Directory, &File, Path, EFI_FILE_MODE_READ, 0);
725 if (!EFI_ERROR (Status)) {
726 //
727 // Found at 'wrong' location - re-use allocated path
728 //
729 File->Close (File);
730 AsciiSPrint ((CHAR8 *)Path, MaxPathSize, "%s%a", DirName, *FileName);
731 AsciiUnixSlashes ((CHAR8 *)Path);
732 FreePool (*FileName);
733 *FileName = (CHAR8 *)Path;
734 return EFI_SUCCESS;
735 }
736
737 DEBUG ((DEBUG_WARN, "LNX: %a not found (searched %s and %s) - %r\n", *FileName, ROOT_DIR, DirName, Status));
738 FreePool (Path);
739 return Status;
740}
741
742STATIC
743BOOLEAN
745 IN OC_FLEX_ARRAY *Options
746 )
747{
748 EFI_STATUS Status;
749 UINTN Index;
750 CHAR8 **Option;
751 CHAR8 *OptionCopy;
752 OC_FLEX_ARRAY *ParsedVars;
753 BOOLEAN HasRoot;
754
755 ASSERT (Options != NULL);
756
757 //
758 // TRUE on errors: do not attempt to add root.
759 //
760 for (Index = 0; Index < Options->Count; Index++) {
761 Option = OcFlexArrayItemAt (Options, Index);
762 ASSERT (Option != NULL);
763 ASSERT (*Option != NULL);
764
765 OptionCopy = AllocateCopyPool (AsciiStrSize (*Option), *Option);
766 if (OptionCopy == NULL) {
767 return TRUE;
768 }
769
770 Status = OcParseVars (OptionCopy, &ParsedVars, OcStringFormatAscii, FALSE);
771 if (EFI_ERROR (Status)) {
772 DEBUG ((DEBUG_WARN, "LNX: Error parsing Options[%u]=<%a> - %r\n", Index, *Option, Status));
773 FreePool (OptionCopy);
774 return TRUE;
775 }
776
777 HasRoot = OcHasParsedVar (ParsedVars, "root", OcStringFormatAscii);
778
779 FreePool (ParsedVars);
780 FreePool (OptionCopy);
781
782 if (HasRoot) {
783 return TRUE;
784 }
785 }
786
787 return FALSE;
788}
789
790STATIC
791BOOLEAN
793 EFI_FILE_HANDLE Directory
794 )
795{
796 EFI_STATUS Status;
797 EFI_FILE_PROTOCOL *OstreeFile;
798
799 Status = OcSafeFileOpen (Directory, &OstreeFile, OSTREE_DIR, EFI_FILE_MODE_READ, 0);
800 if (EFI_ERROR (Status)) {
801 return FALSE;
802 }
803
804 Status = OcEnsureDirectoryFile (OstreeFile, TRUE);
805 if (EFI_ERROR (Status)) {
806 DEBUG ((DEBUG_WARN, "LNX: %s found but not a %a - %r\n", OSTREE_DIR, "directory", Status));
807 }
808
809 OstreeFile->Close (OstreeFile);
810
811 return !EFI_ERROR (Status);
812}
813
814STATIC
815EFI_STATUS
817 EFI_FILE_HANDLE Directory,
818 EFI_FILE_INFO *FileInfo,
819 UINTN FileInfoSize,
820 VOID *Context OPTIONAL
821 )
822{
823 EFI_STATUS Status;
824 CHAR8 *Content;
825 LOADER_ENTRY *Entry;
826 CHAR16 SaveChar;
827 CHAR16 *DirName;
828 CHAR8 **Initrd;
829 UINTN Index;
830
831 ASSERT (Context != NULL);
832 DirName = Context;
833
834 if (FileInfoSize > MAX_LOADER_ENTRY_FILE_INFO_SIZE) {
835 SaveChar = FileInfo->FileName[MAX_LOADER_ENTRY_NAME_LEN];
836 FileInfo->FileName[MAX_LOADER_ENTRY_NAME_LEN] = CHAR_NULL;
837 DEBUG ((DEBUG_WARN, "LNX: Entry filename overlong: %s...\n", FileInfo->FileName));
838 FileInfo->FileName[MAX_LOADER_ENTRY_NAME_LEN] = SaveChar;
839 return EFI_NOT_FOUND;
840 }
841
842 if (FileInfo->FileSize > MAX_LOADER_ENTRY_FILE_SIZE) {
843 DEBUG ((DEBUG_WARN, "LNX: Entry file size overlong: %s\n", FileInfo->FileName));
844 return EFI_NOT_FOUND;
845 }
846
847 DEBUG ((
848 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
849 "LNX: Reading %s...\n",
850 FileInfo->FileName
851 ));
852
853 Content = OcReadFileFromDirectory (Directory, FileInfo->FileName, NULL, 0);
854 if (Content == NULL) {
855 return EFI_OUT_OF_RESOURCES;
856 }
857
859 if (Entry == NULL) {
860 return EFI_OUT_OF_RESOURCES;
861 }
862
863 Status = InternalProcessLoaderEntryFile (FileInfo->FileName, Content, Entry, mIsGrub2);
864 if (EFI_ERROR (Status)) {
866 return Status;
867 }
868
869 if (Entry->Linux == NULL) {
870 DEBUG ((
871 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
872 "LNX: No linux line, ignoring\n"
873 ));
875 return EFI_NOT_FOUND;
876 }
877
878 if (mIsGrub2) {
879 Status = ExpandReplaceOptions (Entry);
880 if (!EFI_ERROR (Status)) {
881 Status = ExpandInitrds (Entry);
882 }
883
884 if (EFI_ERROR (Status)) {
886 return Status;
887 }
888 }
889
890 //
891 // Check all files exist, see comment on FindLoaderFile.
892 //
893 Status = FindLoaderFile (Directory, DirName, &Entry->Linux);
894 if (!EFI_ERROR (Status)) {
895 for (Index = 0; Index < Entry->Initrds->Count; Index++) {
896 Initrd = OcFlexArrayItemAt (Entry->Initrds, Index);
897 Status = FindLoaderFile (Directory, DirName, Initrd);
898 if (EFI_ERROR (Status)) {
899 break;
900 }
901 }
902 }
903
904 if (EFI_ERROR (Status)) {
906 return Status;
907 }
908
909 //
910 // Need to understand other reasons to apply this fix (if any),
911 // so not automatically applying unless we recognise the layout.
912 //
914 && !HasRootOption (Entry->Options))
915 {
916 if (!HasOstreeDir (Directory)) {
917 DEBUG ((DEBUG_WARN, "LNX: Missing root option, %s %afound - %afixing\n", OSTREE_DIR, "not ", "not "));
918 } else {
919 DEBUG ((DEBUG_INFO, "LNX: Missing root option, %s %afound - %afixing\n", OSTREE_DIR, "", ""));
920 Status = InsertRootOption (Entry->Options);
921 if (EFI_ERROR (Status)) {
923 return Status;
924 }
925 }
926 }
927
928 //
929 // Id, and version if not already set within .conf data, from filename.
930 //
931 Status = InternalIdVersionFromFileName (Entry, FileInfo->FileName);
932 if (EFI_ERROR (Status)) {
934 return Status;
935 }
936
937 return EFI_SUCCESS;
938}
939
940STATIC
941EFI_STATUS
943 EFI_FILE_HANDLE Directory,
944 EFI_FILE_INFO *FileInfo,
945 UINTN FileInfoSize,
946 VOID *Context OPTIONAL
947 )
948{
949 EFI_STATUS Status;
950
951 Status = DoFilterLoaderEntry (Directory, FileInfo, FileInfoSize, Context);
952 if (EFI_ERROR (Status)) {
953 return Status;
954 }
955
956 Status = DoProcessLoaderEntry (Directory, FileInfo, FileInfoSize, Context);
957 if (EFI_ERROR (Status)) {
958 DEBUG ((DEBUG_WARN, "LNX: Error processing %s - %r\n", FileInfo->FileName, Status));
959
960 //
961 // Continue to use earlier or later .conf files which we can process -
962 // more chance of a way to boot into Linux for end user.
963 //
964 return EFI_NOT_FOUND;
965 }
966
967 return EFI_SUCCESS;
968}
969
970STATIC
971EFI_STATUS
973 VOID
974 )
975{
976 EFI_STATUS Status;
977 UINTN Index;
978
979 STATIC_ASSERT (ARRAY_SIZE (mTuneDVars) > 0, "No TuneD vars to set");
980
981 Status = EFI_SUCCESS;
982 for (Index = 0; Index < ARRAY_SIZE (mTuneDVars); Index++) {
983 if (InternalGetGrubVar (mTuneDVars[Index]) == NULL) {
984 Status = InternalSetGrubVar (mTuneDVars[Index], "", VAR_ERR_NONE);
985 if (EFI_ERROR (Status)) {
986 break;
987 }
988 }
989 }
990
991 return Status;
992}
993
994STATIC
995EFI_STATUS
997 IN EFI_FILE_PROTOCOL *RootDirectory,
998 IN CHAR16 *DirName,
999 OUT OC_PICKER_ENTRY **Entries,
1000 OUT UINTN *NumEntries
1001 )
1002{
1003 EFI_STATUS Status;
1004 EFI_FILE_PROTOCOL *EntriesDirectory;
1005 CHAR8 *GrubCfg;
1006 CHAR8 *GrubEnv;
1007 GRUB_VAR *EarlyInitrdVar;
1008
1009 Status = OcSafeFileOpen (RootDirectory, &EntriesDirectory, LOADER_ENTRIES_DIR, EFI_FILE_MODE_READ, 0);
1010 if (EFI_ERROR (Status)) {
1011 return Status;
1012 }
1013
1014 Status = EFI_SUCCESS;
1015 GrubEnv = NULL;
1016 mIsGrub2 = FALSE;
1017 gLoaderEntries = NULL;
1018
1019 //
1020 // Treat /loader/entries as being GRUB2+blscfg style if /grub2/grub.cfg exists.
1021 //
1022 GrubCfg = OcReadFileFromDirectory (RootDirectory, GRUB2_GRUB_CFG, NULL, 0);
1023 if (GrubCfg == NULL) {
1024 DEBUG ((DEBUG_INFO, "LNX: %s not found\n", GRUB2_GRUB_CFG));
1025 } else {
1026 mIsGrub2 = TRUE;
1027 Status = InternalInitGrubVars ();
1028 if (!EFI_ERROR (Status)) {
1029 //
1030 // Read grubenv first, since vars in grub.cfg should overwrite it.
1031 //
1032 GrubEnv = OcReadFileFromDirectory (RootDirectory, GRUB2_GRUBENV, NULL, GRUB2_GRUBENV_SIZE);
1033 if (GrubEnv == NULL) {
1034 DEBUG ((DEBUG_WARN, "LNX: %s not found\n", GRUB2_GRUBENV));
1035 } else {
1036 DEBUG ((
1037 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
1038 "LNX: Reading %s\n",
1040 ));
1041 Status = InternalProcessGrubEnv (GrubEnv, GRUB2_GRUBENV_SIZE);
1042 }
1043
1044 if (!EFI_ERROR (Status)) {
1045 DEBUG ((
1046 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
1047 "LNX: Reading %s\n",
1049 ));
1050 Status = InternalProcessGrubCfg (GrubCfg);
1051 }
1052
1053 if ( !EFI_ERROR (Status)
1055 {
1056 DEBUG ((
1057 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
1058 "LNX: Fix TuneD vars\n"
1059 ));
1060 Status = FixTuneDVars ();
1061 }
1062 }
1063 }
1064
1065 //
1066 // If we are grub2 and $early_initrd exists, then warn and halt (blscfg logic is to use it).
1067 // Would not be hard to implement in ExpandInitrds if required. This is a space separated list
1068 // of filenames to use first as initrds.
1069 //
1070 if (!EFI_ERROR (Status)) {
1071 if (mIsGrub2) {
1072 EarlyInitrdVar = InternalGetGrubVar ("early_initrd");
1073 if ((EarlyInitrdVar != NULL) &&
1074 (EarlyInitrdVar->Value != NULL) &&
1075 (EarlyInitrdVar->Value[0] != '\0')
1076 )
1077 {
1078 DEBUG ((DEBUG_INFO, "LNX: grub var $%a is present but currently unsupported - aborting\n", "early_initrd"));
1079 Status = EFI_INVALID_PARAMETER;
1080 }
1081 }
1082 }
1083
1084 if (!EFI_ERROR (Status)) {
1086 if (gLoaderEntries == NULL) {
1087 Status = EFI_OUT_OF_RESOURCES;
1088 } else {
1089 Status = OcScanDirectory (EntriesDirectory, ProcessLoaderEntry, DirName);
1090 }
1091
1092 if (!EFI_ERROR (Status)) {
1094
1096 RootDirectory,
1097 Entries,
1098 NumEntries
1099 );
1100 }
1101
1103 }
1104
1106
1107 if (GrubEnv != NULL) {
1108 FreePool (GrubEnv);
1109 }
1110
1111 if (GrubCfg != NULL) {
1112 FreePool (GrubCfg);
1113 }
1114
1115 EntriesDirectory->Close (EntriesDirectory);
1116
1117 return Status;
1118}
1119
1120STATIC
1121EFI_STATUS
1123 IN EFI_FILE_PROTOCOL *Directory,
1124 IN CHAR16 *DirName,
1125 OUT OC_PICKER_ENTRY **Entries,
1126 OUT UINTN *NumEntries
1127 )
1128{
1129 EFI_STATUS Status;
1130
1131 Status = ScanLoaderEntriesAtDirectory (Directory, DirName, Entries, NumEntries);
1132
1133 DEBUG ((
1134 (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) ? DEBUG_WARN : DEBUG_INFO,
1135 "LNX: ScanLoaderEntries %s - %r\n",
1136 DirName,
1137 Status
1138 ));
1139
1140 return Status;
1141}
1142
1143EFI_STATUS
1145 IN EFI_FILE_PROTOCOL *RootDirectory,
1146 OUT OC_PICKER_ENTRY **Entries,
1147 OUT UINTN *NumEntries
1148 )
1149{
1150 EFI_STATUS Status;
1151 EFI_FILE_PROTOCOL *AdditionalScanDirectory;
1152
1153 Status = DoScanLoaderEntries (RootDirectory, ROOT_DIR, Entries, NumEntries);
1154 if (EFI_ERROR (Status)) {
1155 Status = OcSafeFileOpen (RootDirectory, &AdditionalScanDirectory, BOOT_DIR, EFI_FILE_MODE_READ, 0);
1156 if (!EFI_ERROR (Status)) {
1157 Status = DoScanLoaderEntries (AdditionalScanDirectory, BOOT_DIR, Entries, NumEntries);
1158 AdditionalScanDirectory->Close (AdditionalScanDirectory);
1159 }
1160 }
1161
1162 return Status;
1163}
1164
1165STATIC
1166EFI_STATUS
1168 OUT OC_PICKER_ENTRY **Entries,
1169 OUT UINTN *NumEntries
1170 )
1171{
1172 EFI_STATUS Status;
1173 UINTN Index;
1174 UINTN OptionsIndex;
1175 LOADER_ENTRY *Entry;
1176 OC_FLEX_ARRAY *PickerEntries;
1177 OC_PICKER_ENTRY *PickerEntry;
1178 OC_ASCII_STRING_BUFFER *StringBuffer;
1179 CHAR8 **Options;
1180 UINTN OptionsLength;
1181
1182 StringBuffer = NULL;
1183
1185 if (PickerEntries == NULL) {
1186 return EFI_OUT_OF_RESOURCES;
1187 }
1188
1189 Status = EFI_SUCCESS;
1190 for (Index = 0; Index < gLoaderEntries->Count; Index++) {
1191 Entry = OcFlexArrayItemAt (gLoaderEntries, Index);
1192
1193 PickerEntry = OcFlexArrayAddItem (PickerEntries);
1194 if (PickerEntry == NULL) {
1195 Status = EFI_OUT_OF_RESOURCES;
1196 break;
1197 }
1198
1199 PickerEntry->Path = AllocateCopyPool (AsciiStrSize (Entry->Linux), Entry->Linux);
1200 if (PickerEntry->Path == NULL) {
1201 Status = EFI_OUT_OF_RESOURCES;
1202 break;
1203 }
1204
1205 //
1206 // Arguments.
1207 //
1208 StringBuffer = OcAsciiStringBufferInit ();
1209 if (StringBuffer == NULL) {
1210 Status = EFI_OUT_OF_RESOURCES;
1211 break;
1212 }
1213
1214 for (OptionsIndex = 0; OptionsIndex < Entry->Initrds->Count; OptionsIndex++) {
1215 Options = OcFlexArrayItemAt (Entry->Initrds, OptionsIndex);
1216 Status = OcAsciiStringBufferAppend (StringBuffer, "initrd=");
1217 if (EFI_ERROR (Status)) {
1218 break;
1219 }
1220
1221 Status = OcAsciiStringBufferAppend (StringBuffer, *Options);
1222 if (EFI_ERROR (Status)) {
1223 break;
1224 }
1225
1226 Status = OcAsciiStringBufferAppend (StringBuffer, " ");
1227 if (EFI_ERROR (Status)) {
1228 break;
1229 }
1230 }
1231
1232 if (EFI_ERROR (Status)) {
1233 break;
1234 }
1235
1236 for (OptionsIndex = 0; OptionsIndex < Entry->Options->Count; OptionsIndex++) {
1237 Options = OcFlexArrayItemAt (Entry->Options, OptionsIndex);
1238 OptionsLength = AsciiStrLen (*Options);
1239 ASSERT (OptionsLength != 0);
1240 if (OptionsLength == 0) {
1241 continue;
1242 }
1243
1244 if ((*Options)[OptionsLength - 1] == ' ') {
1245 --OptionsLength;
1246 }
1247
1248 Status = OcAsciiStringBufferAppendN (StringBuffer, *Options, OptionsLength);
1249 if (EFI_ERROR (Status)) {
1250 break;
1251 }
1252
1253 if (OptionsIndex < Entry->Options->Count - 1) {
1254 Status = OcAsciiStringBufferAppend (StringBuffer, " ");
1255 if (EFI_ERROR (Status)) {
1256 break;
1257 }
1258 }
1259 }
1260
1261 if (EFI_ERROR (Status)) {
1262 break;
1263 }
1264
1265 PickerEntry->Arguments = OcAsciiStringBufferFreeContainer (&StringBuffer);
1266
1267 //
1268 // Name.
1269 //
1270 StringBuffer = OcAsciiStringBufferInit ();
1271 if (StringBuffer == NULL) {
1272 Status = EFI_OUT_OF_RESOURCES;
1273 break;
1274 }
1275
1277 //
1278 // Show file system type and enough of PARTUUID to help distinguish one partition from another.
1279 //
1280 Status = OcAsciiStringBufferSPrint (StringBuffer, "%a-%08x", gFileSystemType, gPartuuid.Data1);
1281 if (EFI_ERROR (Status)) {
1282 break;
1283 }
1284
1285 OcAsciiToLower (&StringBuffer->String[StringBuffer->StringLength] - sizeof (gPartuuid.Data1) * 2);
1286
1287 Status = OcAsciiStringBufferAppend (StringBuffer, ": ");
1288 if (EFI_ERROR (Status)) {
1289 break;
1290 }
1291 }
1292
1293 Status = OcAsciiStringBufferAppend (StringBuffer, Entry->Title);
1294 if (EFI_ERROR (Status)) {
1295 break;
1296 }
1297
1298 PickerEntry->Name = OcAsciiStringBufferFreeContainer (&StringBuffer);
1299
1300 ASSERT (Entry->OcFlavour != NULL);
1301 PickerEntry->Flavour = AllocateCopyPool (AsciiStrSize (Entry->OcFlavour), Entry->OcFlavour);
1302 if (PickerEntry->Flavour == NULL) {
1303 Status = EFI_OUT_OF_RESOURCES;
1304 break;
1305 }
1306
1307 ASSERT (Entry->OcId != NULL);
1308 PickerEntry->Id = AllocateCopyPool (AsciiStrSize (Entry->OcId), Entry->OcId);
1309 if (PickerEntry->Id == NULL) {
1310 Status = EFI_OUT_OF_RESOURCES;
1311 break;
1312 }
1313
1314 PickerEntry->Auxiliary = Entry->OcAuxiliary;
1315
1316 PickerEntry->RealPath = TRUE;
1317 PickerEntry->TextMode = FALSE;
1318 PickerEntry->Tool = FALSE;
1319 }
1320
1321 if (EFI_ERROR (Status)) {
1322 if (StringBuffer != NULL) {
1323 OcAsciiStringBufferFree (&StringBuffer);
1324 }
1325
1326 OcFlexArrayFree (&PickerEntries);
1327 } else {
1328 ASSERT (StringBuffer == NULL);
1329 OcFlexArrayFreeContainer (&PickerEntries, (VOID **)Entries, NumEntries);
1330 }
1331
1332 return Status;
1333}
1334
1335STATIC
1336EFI_STATUS
1338 LOADER_ENTRY *Entry
1339 )
1340{
1341 EFI_STATUS Status;
1342 OC_ASCII_STRING_BUFFER *StringBuffer;
1343
1344 ASSERT (Entry->Title != NULL);
1345
1346 if ((Entry->Version == NULL) || (AsciiStrStr (Entry->Title, Entry->Version) != NULL)) {
1347 return EFI_SUCCESS;
1348 }
1349
1350 StringBuffer = OcAsciiStringBufferInit ();
1351
1352 Status = OcAsciiStringBufferSPrint (StringBuffer, "%a (%a)", Entry->Title, Entry->Version);
1353
1354 if (!EFI_ERROR (Status)) {
1355 FreePool (Entry->Title);
1356 Entry->Title = OcAsciiStringBufferFreeContainer (&StringBuffer);
1357 }
1358
1359 return Status;
1360}
1361
1362STATIC
1363EFI_STATUS
1365 VOID
1366 )
1367{
1368 EFI_STATUS Status;
1369 UINTN Index;
1370 LOADER_ENTRY *Entry;
1371
1373 return EFI_SUCCESS;
1374 }
1375
1376 for (Index = 0; Index < gLoaderEntries->Count; Index++) {
1377 Entry = OcFlexArrayItemAt (gLoaderEntries, Index);
1378
1379 Status = AppendVersion (Entry);
1380 if (EFI_ERROR (Status)) {
1381 return Status;
1382 }
1383 }
1384
1385 return EFI_SUCCESS;
1386}
1387
1388STATIC
1389EFI_STATUS
1391 VOID
1392 )
1393{
1394 UINTN Index;
1395 UINTN MatchIndex;
1396 LOADER_ENTRY *Entry;
1397 LOADER_ENTRY *MatchEntry;
1398
1399 //
1400 // This check should not be strictly necessary, since there shouldn't
1401 // (normally) be any duplicate ids if this flag is not set.
1402 //
1404 return EFI_SUCCESS;
1405 }
1406
1407 for (Index = 0; Index < gLoaderEntries->Count - 1; Index++) {
1408 Entry = OcFlexArrayItemAt (gLoaderEntries, Index);
1409
1410 if (Entry->DuplicateIdScanned) {
1411 continue;
1412 }
1413
1414 for (MatchIndex = Index + 1; MatchIndex < gLoaderEntries->Count; MatchIndex++) {
1415 MatchEntry = OcFlexArrayItemAt (gLoaderEntries, MatchIndex);
1416
1417 if (!MatchEntry->DuplicateIdScanned) {
1418 if (AsciiStrCmp (Entry->OcId, MatchEntry->OcId) == 0) {
1419 //
1420 // Everything but the first id duplicate becomes auxiliary.
1421 //
1422 MatchEntry->OcAuxiliary = TRUE;
1423 MatchEntry->DuplicateIdScanned = TRUE;
1424 }
1425 }
1426 }
1427 }
1428
1429 return EFI_SUCCESS;
1430}
1431
1432STATIC
1433EFI_STATUS
1435 IN EFI_FILE_PROTOCOL *RootDirectory,
1436 IN LOADER_ENTRY *Entry
1437 )
1438{
1439 CHAR8 *Variant;
1440 UINTN Length;
1441 UINTN NumPrinted;
1442
1443 Variant = NULL;
1444
1445 if (Entry->Title != NULL) {
1446 Variant = ExtractVariantFrom (Entry->Title);
1447 }
1448
1449 ASSERT (Entry->OcFlavour == NULL);
1450
1451 if (Variant != NULL) {
1452 Length = AsciiStrLen (Variant) + L_STR_LEN ("Linux") + 1;
1453
1454 Entry->OcFlavour = AllocatePool (Length + 1);
1455 if (Entry->OcFlavour == NULL) {
1456 return EFI_OUT_OF_RESOURCES;
1457 }
1458
1459 NumPrinted = AsciiSPrint (Entry->OcFlavour, Length + 1, "%a:%a", Variant, "Linux");
1460 ASSERT (NumPrinted == Length);
1461 } else {
1462 Variant = "Linux";
1463 Entry->OcFlavour = AllocateCopyPool (AsciiStrSize (Variant), Variant);
1464 if (Entry->OcFlavour == NULL) {
1465 return EFI_OUT_OF_RESOURCES;
1466 }
1467 }
1468
1469 if (Entry->Title == NULL) {
1470 Entry->Title = AllocateCopyPool (AsciiStrSize (Variant), Variant);
1471 if (Entry->Title == NULL) {
1472 return EFI_OUT_OF_RESOURCES;
1473 }
1474 }
1475
1476 return EFI_SUCCESS;
1477}
1478
1479STATIC
1480EFI_STATUS
1482 IN EFI_FILE_PROTOCOL *RootDirectory
1483 )
1484{
1485 EFI_STATUS Status;
1486 UINTN Index;
1487 LOADER_ENTRY *Entry;
1488
1489 Status = EFI_SUCCESS;
1490
1491 for (Index = 0; Index < gLoaderEntries->Count; Index++) {
1492 Entry = OcFlexArrayItemAt (gLoaderEntries, Index);
1493
1494 Status = EntryApplyDefaults (RootDirectory, Entry);
1495 if (EFI_ERROR (Status)) {
1496 break;
1497 }
1498 }
1499
1500 return Status;
1501}
1502
1503//
1504// Also use for loader entries generated by autodetect.
1505//
1506EFI_STATUS
1508 IN EFI_FILE_PROTOCOL *RootDirectory,
1509 OUT OC_PICKER_ENTRY **Entries,
1510 OUT UINTN *NumEntries
1511 )
1512{
1513 EFI_STATUS Status;
1514
1515 Status = EFI_SUCCESS;
1516
1517 //
1518 // Sort entries by version descending.
1519 //
1521
1522 //
1523 // Autodetect good title and flavour, if needed.
1524 //
1525 Status = ApplyDefaults (RootDirectory);
1526
1527 //
1528 // Always append version to titles when !HideAuxiliary.
1529 // We previously implemented a pure-BLSpec process of disambiguating by appending version
1530 // only if the title is ambiguous with others on the same partition, but it is more natural
1531 // to do it always.
1532 //
1533 if (!EFI_ERROR (Status)) {
1534 Status = AppendVersions ();
1535 }
1536
1537 //
1538 // Make duplicates by id after the first into auxiliary entries.
1539 //
1540 if (!EFI_ERROR (Status)) {
1541 Status = DisambiguateDuplicates ();
1542 }
1543
1544 if (!EFI_ERROR (Status)) {
1546 Entries,
1547 NumEntries
1548 );
1549 }
1550
1551 return Status;
1552}
#define ARRAY_SIZE(Array)
Definition AppleMacEfi.h:34
UINT32 Version
UINT64 Length
EFI_STATUS InsertRootOption(IN OC_FLEX_ARRAY *Options)
Definition Autodetect.c:444
EFI_STATUS InternalProcessGrubCfg(IN OUT CHAR8 *Content)
Definition GrubCfg.c:398
EFI_STATUS InternalProcessGrubEnv(IN OUT CHAR8 *Content, IN CONST UINTN Length)
Definition GrubEnv.c:23
EFI_STATUS InternalInitGrubVars(VOID)
Definition GrubVars.c:21
EFI_STATUS InternalSetGrubVar(CHAR8 *Key, CHAR8 *Value, UINTN Errors)
Definition GrubVars.c:44
VOID InternalFreeGrubVars(VOID)
Definition GrubVars.c:34
EFI_STATUS InternalExpandGrubVars(IN CONST CHAR8 *Value, IN OUT CHAR8 **Result)
Definition GrubVars.c:172
GRUB_VAR * InternalGetGrubVar(IN CONST CHAR8 *Key)
Definition GrubVars.c:128
EFI_STATUS InternalExpandGrubVarsForArray(IN OUT OC_FLEX_ARRAY *Options)
Definition GrubVars.c:146
#define LINUX_BOOT_ALLOW_CONF_AUTO_ROOT
OC_FLEX_ARRAY * gLoaderEntries
EFI_GUID gPartuuid
VOID InternalFreePickerEntry(IN OC_PICKER_ENTRY *Entry)
#define LINUX_BOOT_FIX_TUNED
#define LINUX_BOOT_ADD_DEBUG_INFO
UINTN gLinuxBootFlags
CHAR8 * gFileSystemType
#define LINUX_BOOT_USE_LATEST
INTN EFIAPI InternalReverseVersionCompare(IN CONST VOID *Version1, IN CONST VOID *Version2)
OC_PICKER_CONTEXT * gPickerContext
#define VAR_ERR_NONE
#define LINUX_BOOT_LOG_VERBOSE
enum ENTRY_PARSE_STATE_ ENTRY_PARSE_STATE
STATIC BOOLEAN HasOstreeDir(EFI_FILE_HANDLE Directory)
STATIC CHAR8 * mLinuxVariants[]
STATIC EFI_STATUS DoProcessLoaderEntry(EFI_FILE_HANDLE Directory, EFI_FILE_INFO *FileInfo, UINTN FileInfoSize, VOID *Context OPTIONAL)
#define LOADER_ENTRIES_DIR
Definition LoaderEntry.c:53
STATIC EFI_STATUS ProcessLoaderEntry(EFI_FILE_HANDLE Directory, EFI_FILE_INFO *FileInfo, UINTN FileInfoSize, VOID *Context OPTIONAL)
EFI_STATUS InternalScanLoaderEntries(IN EFI_FILE_PROTOCOL *RootDirectory, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
STATIC EFI_STATUS DoConvertLoaderEntriesToBootEntries(OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
STATIC EFI_STATUS AppendVersions(VOID)
#define GRUB2_GRUB_CFG
Definition LoaderEntry.c:54
STATIC BOOLEAN mIsGrub2
Definition LoaderEntry.c:86
STATIC EFI_STATUS ApplyDefaults(IN EFI_FILE_PROTOCOL *RootDirectory)
STATIC EFI_STATUS ExpandInitrds(IN OUT LOADER_ENTRY *Entry)
STATIC EFI_STATUS ExpandReplaceOptions(IN OUT LOADER_ENTRY *Entry)
EFI_STATUS InternalConvertLoaderEntriesToBootEntries(IN EFI_FILE_PROTOCOL *RootDirectory, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
#define ROOT_DIR
Definition LoaderEntry.c:37
STATIC EFI_STATUS DoScanLoaderEntries(IN EFI_FILE_PROTOCOL *Directory, IN CHAR16 *DirName, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
VOID InternalFreeLoaderEntry(LOADER_ENTRY *Entry)
#define MAX_LOADER_ENTRY_NAME_LEN
Definition LoaderEntry.c:68
#define MAX_LOADER_ENTRY_FILE_INFO_SIZE
Definition LoaderEntry.c:69
#define BLSPEC_PREFIX_AUTO
Definition LoaderEntry.c:59
ENTRY_PARSE_STATE_
Definition LoaderEntry.c:91
@ ENTRY_VALUE
Definition LoaderEntry.c:96
@ ENTRY_KEY
Definition LoaderEntry.c:94
@ ENTRY_KEY_VALUE_SPACE
Definition LoaderEntry.c:95
@ ENTRY_COMMENT
Definition LoaderEntry.c:93
@ ENTRY_LEADING_SPACE
Definition LoaderEntry.c:92
STATIC EFI_STATUS EntryApplyDefaults(IN EFI_FILE_PROTOCOL *RootDirectory, IN LOADER_ENTRY *Entry)
STATIC EFI_STATUS FindLoaderFile(IN CONST EFI_FILE_HANDLE Directory, IN CONST CHAR16 *DirName, IN OUT CHAR8 **FileName)
STATIC EFI_STATUS DisambiguateDuplicates(VOID)
STATIC EFI_STATUS ScanLoaderEntriesAtDirectory(IN EFI_FILE_PROTOCOL *RootDirectory, IN CHAR16 *DirName, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
#define MAX_LOADER_ENTRY_FILE_SIZE
Definition LoaderEntry.c:78
STATIC BOOLEAN HasRootOption(IN OC_FLEX_ARRAY *Options)
STATIC EFI_STATUS FixTuneDVars(VOID)
STATIC EFI_STATUS EntryCopyMultipleValue(IN CONST BOOLEAN Grub2, IN OC_FLEX_ARRAY *Array, IN CONST CHAR8 *Value)
STATIC EFI_STATUS AppendVersion(LOADER_ENTRY *Entry)
#define GRUB2_GRUBENV_SIZE
Definition LoaderEntry.c:56
LOADER_ENTRY * InternalAllocateLoaderEntry(VOID)
STATIC EFI_STATUS GetLoaderEntryLine(IN CONST CHAR16 *FileName, IN OUT CHAR8 *Content, IN OUT UINTN *Pos, OUT CHAR8 **Key, OUT CHAR8 **Value)
STATIC CHAR8 * ExtractVariantFrom(IN CHAR8 *String)
#define BLSPEC_SUFFIX_CONF
Definition LoaderEntry.c:58
#define BOOT_DIR
Definition LoaderEntry.c:43
EFI_STATUS InternalIdVersionFromFileName(IN OUT LOADER_ENTRY *Entry, IN CHAR16 *FileName)
#define OSTREE_DIR
Definition LoaderEntry.c:47
STATIC EFI_STATUS EntryCopySingleValue(IN CONST BOOLEAN Grub2, IN OUT CHAR8 **Target, IN CONST CHAR8 *Value)
#define GRUB2_GRUBENV
Definition LoaderEntry.c:55
STATIC EFI_STATUS DoFilterLoaderEntry(EFI_FILE_HANDLE Directory, EFI_FILE_INFO *FileInfo, UINTN FileInfoSize, VOID *Context OPTIONAL)
STATIC CHAR8 * mTuneDVars[]
Definition LoaderEntry.c:29
EFI_STATUS InternalProcessLoaderEntryFile(IN CONST CHAR16 *FileName, IN OUT CHAR8 *Content, OUT LOADER_ENTRY *Entry, IN CONST BOOLEAN Grub2)
STATIC_ASSERT(BYTES_PER_PIXEL==sizeof(UINT32), "Non 4-byte pixels are unsupported!")
OC_PARSED_VAR * OcParsedVarsItemAt(IN CONST OC_FLEX_ARRAY *ParsedVars, IN CONST UINTN Index)
BOOLEAN OcHasParsedVar(IN CONST OC_FLEX_ARRAY *ParsedVars, IN CONST VOID *Name, IN CONST OC_STRING_FORMAT StringFormat)
EFI_STATUS OcParseVars(IN VOID *StrVars, OUT OC_FLEX_ARRAY **ParsedVars, IN CONST OC_STRING_FORMAT StringFormat, IN CONST BOOLEAN TokensOnly)
EFI_STATUS OcEnsureDirectoryFile(IN EFI_FILE_PROTOCOL *File, IN BOOLEAN IsDirectory)
VOID * OcReadFileFromDirectory(IN CONST EFI_FILE_PROTOCOL *RootDirectory, IN CONST CHAR16 *FilePath, OUT UINT32 *FileSize OPTIONAL, IN UINT32 MaxFileSize OPTIONAL)
Definition ReadFile.c:155
EFI_STATUS OcScanDirectory(IN EFI_FILE_HANDLE Directory, IN OC_PROCESS_DIRECTORY_ENTRY ProcessEntry, IN OUT VOID *Context OPTIONAL)
EFI_STATUS OcSafeFileOpen(IN CONST EFI_FILE_PROTOCOL *Directory, OUT EFI_FILE_PROTOCOL **NewHandle, IN CONST CHAR16 *FileName, IN CONST UINT64 OpenMode, IN CONST UINT64 Attributes)
Definition OpenFile.c:29
VOID OcFlexArrayFree(IN OUT OC_FLEX_ARRAY **FlexArray)
CHAR8 * OcAsciiStringBufferFreeContainer(IN OUT OC_ASCII_STRING_BUFFER **StringBuffer)
OC_FLEX_ARRAY * OcFlexArrayInit(IN CONST UINTN ItemSize, IN CONST OC_FLEX_ARRAY_FREE_ITEM FreeItem OPTIONAL)
Definition FlexArray.c:31
VOID(* OC_FLEX_ARRAY_FREE_ITEM)(IN VOID *Item)
EFI_STATUS OcAsciiStringBufferAppendN(IN OUT OC_ASCII_STRING_BUFFER *Buffer, IN CONST CHAR8 *AppendString, OPTIONAL IN CONST UINTN Length)
VOID OcFlexArrayFreePointerItem(IN VOID *Item)
Definition FlexArray.c:18
VOID OcFlexArrayFreeContainer(IN OUT OC_FLEX_ARRAY **FlexArray, IN OUT VOID **Items, IN OUT UINTN *Count)
VOID * OcFlexArrayAddItem(IN OUT OC_FLEX_ARRAY *FlexArray)
Definition FlexArray.c:136
EFI_STATUS OcAsciiStringBufferAppend(IN OUT OC_ASCII_STRING_BUFFER *Buffer, IN CONST CHAR8 *AppendString OPTIONAL)
VOID * OcFlexArrayItemAt(IN CONST OC_FLEX_ARRAY *FlexArray, IN CONST UINTN Index)
Definition FlexArray.c:189
VOID OcFlexArrayDiscardItem(IN OUT OC_FLEX_ARRAY *FlexArray, IN CONST BOOLEAN FreeItem)
Definition FlexArray.c:238
EFI_STATUS EFIAPI OcAsciiStringBufferSPrint(IN OUT OC_ASCII_STRING_BUFFER *Buffer, IN CONST CHAR8 *FormatString,...)
OC_ASCII_STRING_BUFFER * OcAsciiStringBufferInit(VOID)
VOID OcAsciiStringBufferFree(IN OUT OC_ASCII_STRING_BUFFER **StringBuffer)
CHAR8 * OcAsciiToLower(CHAR8 *Str)
Definition OcAsciiLib.c:555
CHAR16 *EFIAPI OcStrChr(IN CONST CHAR16 *String, IN CHAR16 Char)
VOID AsciiUnixSlashes(IN OUT CHAR8 *String)
Definition OcAsciiLib.c:99
#define L_STR_LEN(String)
Definition OcStringLib.h:26
VOID UnicodeUefiSlashes(IN OUT CHAR16 *String)
CHAR8 *EFIAPI OcAsciiStriStr(IN CONST CHAR8 *String, IN CONST CHAR8 *SearchString)
Definition OcAsciiLib.c:327
@ OcStringFormatAscii
Definition OcStringLib.h:50
BOOLEAN EFIAPI OcUnicodeEndsWith(IN CONST CHAR16 *String, IN CONST CHAR16 *SearchString, IN BOOLEAN CaseInsensitiveMatch)
#define ASSERT(x)
Definition coder.h:55
ush Pos
Definition deflate.h:92
OC_FLEX_ARRAY * Initrds
OC_FLEX_ARRAY * Options
CONST CHAR8 * Arguments