OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
Autodetect.c
Go to the documentation of this file.
1
8#include "LinuxBootInternal.h"
9
10#include <Uefi.h>
11#include <Guid/Gpt.h>
12#include <Library/BaseLib.h>
13#include <Library/DevicePathLib.h>
14#include <Library/MemoryAllocationLib.h>
17#include <Library/OcFileLib.h>
19#include <Library/OcStringLib.h>
20#include <Library/PrintLib.h>
21#include <Library/UefiBootServicesTableLib.h>
22
24
25#define GRUB_DEFAULT_FILE L"\\etc\\default\\grub"
26#define OS_RELEASE_FILE L"\\etc\\os-release"
27#define AUTODETECT_DIR L"\\boot"
28#define ROOT_DIR L"\\"
29#define ROOT_FS_FILE L"\\bin\\sh"
30
31STATIC
34
35STATIC
38
39STATIC
42
43STATIC
44CHAR8
46
47STATIC
48CHAR8
50
51STATIC
52CHAR8
54
55STATIC
58
59STATIC
60CHAR8
62
63STATIC
66
67STATIC
68CHAR16
70
71STATIC
72CHAR16
74
75STATIC
76CHAR16
78
79STATIC
80CHAR16
82
83STATIC
84EFI_STATUS
86 EFI_FILE_HANDLE Directory,
87 EFI_FILE_INFO *FileInfo,
88 UINTN FileInfoSize,
89 VOID *Context OPTIONAL
90 )
91{
92 CHAR16 *Dash;
93 VMLINUZ_FILE *VmlinuzFile;
94
95 if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
96 return EFI_NOT_FOUND;
97 }
98
99 //
100 // Do not use files without '-' in the name, i.e. we do not need and
101 // do not try to use `vmlinuz` or `initrd` symlinks even if present
102 // (and even though we can in fact specify them as filenames and boot
103 // fine from them).
104 //
105 Dash = OcStrChr (FileInfo->FileName, L'-');
106 if ((Dash == NULL) || (Dash[1] == L'\0')) {
107 return EFI_NOT_FOUND;
108 }
109
110 if (StrnCmp (L"vmlinuz", FileInfo->FileName, L_STR_LEN (L"vmlinuz")) == 0) {
111 VmlinuzFile = OcFlexArrayAddItem (mVmlinuzFiles);
112 } else if (StrnCmp (L"init", FileInfo->FileName, L_STR_LEN (L"init")) == 0) {
113 //
114 // initrd* or initramfs*
115 //
116 VmlinuzFile = OcFlexArrayAddItem (mInitrdFiles);
117 } else {
118 return EFI_NOT_FOUND;
119 }
120
121 DEBUG ((
122 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
123 "LNX: Found %s...\n",
124 FileInfo->FileName
125 ));
126
127 if (VmlinuzFile == NULL) {
128 return EFI_OUT_OF_RESOURCES;
129 }
130
131 VmlinuzFile->FileName = AllocateCopyPool (StrSize (FileInfo->FileName), FileInfo->FileName);
132 if (VmlinuzFile->FileName == NULL) {
133 return EFI_OUT_OF_RESOURCES;
134 }
135
136 VmlinuzFile->Version = &VmlinuzFile->FileName[&Dash[1] - FileInfo->FileName];
137 VmlinuzFile->StrLen = StrLen (FileInfo->FileName);
138
139 return EFI_SUCCESS;
140}
141
142STATIC
143EFI_STATUS
145 CHAR8 **Dest,
146 CHAR16 *DirectoryPath,
147 UINTN DirectoryPathLength,
148 CHAR16 *FilePath,
149 UINTN FilePathLength
150 )
151{
152 UINTN Size;
153 BOOLEAN UseDir;
154
155 UseDir = !(DirectoryPathLength == 1 && DirectoryPath[0] == L'\\');
156
157 Size = (UseDir ? DirectoryPathLength : 0) + FilePathLength + 2;
158 *Dest = AllocatePool (Size);
159
160 if (*Dest == NULL) {
161 return EFI_OUT_OF_RESOURCES;
162 }
163
164 AsciiSPrint (*Dest, Size, "%s\\%s", UseDir ? DirectoryPath : L"", FilePath);
165
166 return EFI_SUCCESS;
167}
168
169STATIC
170EFI_STATUS
172 CHAR8 **Dest
173 )
174{
175 UINTN Length;
176 UINTN NumPrinted;
177
178 Length = L_STR_LEN ("root=PARTUUID=") + GUID_STRING_LENGTH;
179
180 *Dest = AllocatePool (Length + 1);
181 if (*Dest == NULL) {
182 return EFI_OUT_OF_RESOURCES;
183 }
184
185 NumPrinted = AsciiSPrint (*Dest, Length + 1, "%a%g", "root=PARTUUID=", &gPartuuid);
186 ASSERT (NumPrinted == Length);
187
188 //
189 // Value is case-sensitive and must be lower case.
190 //
191 OcAsciiToLower (&(*Dest)[L_STR_LEN ("root=PARTUUID=")]);
192
193 return EFI_SUCCESS;
194}
195
196STATIC
197VOID
199 VOID
200 )
201{
202 UINTN Index;
203 BOOLEAN Found;
204
205 if (mEtcOsReleaseOptions != NULL) {
206 //
207 // If neither are present, default title gets set later to "Linux".
208 //
209 Found = FALSE;
210 for (Index = 0; Index < 2; Index++) {
213 (Index == 0) ? "PRETTY_NAME" : "NAME",
215 )
216 && (mPrettyName != NULL))
217 {
218 Found = TRUE;
219 break;
220 }
221 }
222
223 if (Found) {
224 DEBUG ((
225 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
226 "LNX: Found distro %a\n",
228 ));
229 } else {
230 DEBUG ((DEBUG_WARN, "LNX: Neither %a nor %a found in %s\n", "PRETTY_NAME", "NAME", OS_RELEASE_FILE));
231 }
232 }
233}
234
235STATIC
236EFI_STATUS
238 IN CONST EFI_FILE_PROTOCOL *RootDirectory,
239 IN CONST BOOLEAN IsStandaloneBoot
240 )
241{
242 EFI_STATUS Status;
243 BOOLEAN TryReading;
244
247 mPrettyName = NULL;
248
249 TryReading = (mDiskLabel == NULL);
250
251 DEBUG_CODE_BEGIN ();
252 TryReading = TRUE;
253 DEBUG_CODE_END ();
254
255 if (IsStandaloneBoot) {
256 TryReading = FALSE;
257 }
258
259 //
260 // Load distro name from /etc/os-release.
261 //
262 if (TryReading) {
264 if (mEtcOsReleaseFileContents == NULL) {
265 DEBUG ((DEBUG_WARN, "LNX: %s not found\n", OS_RELEASE_FILE));
266 } else {
267 DEBUG ((
268 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
269 "LNX: Reading %s\n",
271 ));
273 if (EFI_ERROR (Status)) {
274 FreePool (mEtcOsReleaseFileContents);
276 DEBUG ((DEBUG_WARN, "LNX: Cannot parse %s - %r\n", OS_RELEASE_FILE, Status));
277 return Status;
278 }
279
280 //
281 // Do this early to give a nicer log entry order: distro name from os-release is logged
282 // before reports about distro (e.g. before the error below if it is not GRUB-based).
283 //
285 }
286 }
287
288 if (mDiskLabel != NULL) {
290 }
291
292 return EFI_SUCCESS;
293}
294
295STATIC
296EFI_STATUS
298 IN CONST EFI_FILE_PROTOCOL *RootDirectory,
299 IN CONST BOOLEAN IsStandaloneBoot
300 )
301{
302 EFI_STATUS Status;
303
306
307 if (IsStandaloneBoot) {
308 return EFI_SUCCESS;
309 }
310
311 //
312 // Load kernel options from /etc/default/grub.
313 //
315 if (mEtcDefaultGrubFileContents == NULL) {
316 DEBUG ((DEBUG_INFO, "LNX: %s not found (bootloader is not GRUB?)\n", GRUB_DEFAULT_FILE));
317 } else {
318 DEBUG ((
319 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
320 "LNX: Reading %s\n",
322 ));
324 if (EFI_ERROR (Status)) {
327 DEBUG ((DEBUG_WARN, "LNX: Cannot parse %s - %r\n", GRUB_DEFAULT_FILE, Status));
328 return Status;
329 }
330 }
331
332 return EFI_SUCCESS;
333}
334
335STATIC
336VOID
338 IN CONST EFI_FILE_PROTOCOL *VmlinuzDirectory
339 )
340{
341 mDiskLabel = NULL;
342
344 mDiskLabel = OcReadFileFromDirectory (VmlinuzDirectory, L".contentDetails", NULL, 0);
345 if (mDiskLabel == NULL) {
346 mDiskLabel = OcReadFileFromDirectory (VmlinuzDirectory, L".disk_label.contentDetails", NULL, 0);
347 }
348
349 if (mDiskLabel == NULL) {
350 DEBUG ((DEBUG_INFO, "LNX: %s %s not present\n", L".contentDetails", L".disk_label.contentDetails"));
351 } else {
352 DEBUG ((DEBUG_INFO, "LNX: Found disk label '%a'\n", mDiskLabel));
353 }
354 }
355}
356
357STATIC
358VOID
360 VOID
361 )
362{
363 //
364 // If non-null, refers to string inside mEtcOsReleaseFileContents or copy of mDiskLabel.
365 //
366 mPrettyName = NULL;
367
368 if (mDiskLabel != NULL) {
369 FreePool (mDiskLabel);
370 mDiskLabel = NULL;
371 }
372
374 if (mEtcOsReleaseFileContents != NULL) {
375 FreePool (mEtcOsReleaseFileContents);
377 }
378
380 if (mEtcDefaultGrubFileContents != NULL) {
383 }
384}
385
386STATIC
387EFI_STATUS
389 IN CONST UINTN InsertIndex,
390 IN OC_FLEX_ARRAY *Options,
391 IN CONST VOID *Value,
392 IN CONST OC_STRING_FORMAT StringFormat
393 )
394{
395 EFI_STATUS Status;
396 UINTN OptionsLength;
397 UINTN CopiedLength;
398 CHAR8 **Option;
399
400 if (StringFormat == OcStringFormatUnicode) {
401 OptionsLength = StrLen (Value);
402 } else {
403 OptionsLength = AsciiStrLen (Value);
404 }
405
406 if (OptionsLength > 0) {
407 Option = OcFlexArrayInsertItem (Options, InsertIndex);
408 if (Option == NULL) {
409 return EFI_OUT_OF_RESOURCES;
410 }
411
412 if (StringFormat == OcStringFormatUnicode) {
413 *Option = AllocatePool ((OptionsLength + 1) * sizeof (CHAR16));
414 if (*Option == NULL) {
415 return EFI_OUT_OF_RESOURCES;
416 }
417
418 Status = UnicodeStrnToAsciiStrS (Value, OptionsLength, *Option, OptionsLength + 1, &CopiedLength);
419 ASSERT_EFI_ERROR (Status);
420 ASSERT (CopiedLength == OptionsLength);
421 } else {
422 *Option = AllocateCopyPool (OptionsLength + 1, Value);
423 if (*Option == NULL) {
424 return EFI_OUT_OF_RESOURCES;
425 }
426 }
427 }
428
429 return EFI_SUCCESS;
430}
431
432STATIC
433EFI_STATUS
435 IN OC_FLEX_ARRAY *Options,
436 IN CONST VOID *Value,
437 IN CONST OC_STRING_FORMAT StringFormat
438 )
439{
440 return InsertOption (Options->Count, Options, Value, StringFormat);
441}
442
443EFI_STATUS
445 IN OC_FLEX_ARRAY *Options
446 )
447{
448 CHAR8 **NewOption;
449
450 DEBUG ((
451 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
452 "LNX: Creating \"root=PARTUUID=%g\"\n",
453 &gPartuuid
454 ));
455
456 NewOption = OcFlexArrayInsertItem (Options, 0);
457 if (NewOption == NULL) {
458 return EFI_OUT_OF_RESOURCES;
459 }
460
461 return CreateRootPartuuid (NewOption);
462}
463
464STATIC
465VOID
467 VOID
468 )
469{
470 UINTN Index;
471 AUTOOPTS *AutoOpts;
472
475
476 for (Index = 0; Index < mPerPartuuidAutoOpts->Count; Index++) {
477 AutoOpts = OcFlexArrayItemAt (mPerPartuuidAutoOpts, Index);
478 if (CompareMem (&gPartuuid, &AutoOpts->Guid, sizeof (EFI_GUID)) == 0) {
479 if (AutoOpts->PlusOpts) {
481 } else {
482 mCurrentPartuuidAutoOpts = AutoOpts->Opts;
483 }
484 }
485 }
486}
487
488EFI_STATUS
490 IN OC_FLEX_ARRAY *Options
491 )
492{
493 EFI_STATUS Status;
494 UINTN Index;
495 OC_PARSED_VAR *Option;
496 AUTOOPTS *AutoOpts;
497
498 mGlobalAutoOpts = NULL;
499 mGlobalAutoOptsPlus = NULL;
500
502 if (mPerPartuuidAutoOpts == NULL) {
503 return EFI_OUT_OF_RESOURCES;
504 }
505
506 if (Options == NULL) {
507 return EFI_SUCCESS;
508 }
509
510 //
511 // Look for autoopts.
512 // Remember that although args are ASCII in the OC config file, they are
513 // Unicode by the time they get passed as UEFI LoadOptions.
514 //
515 for (Index = 0; Index < Options->Count; Index++) {
516 Option = OcFlexArrayItemAt (Options, Index);
517 //
518 // autoopts:{partuuid}[+]="...": user options for specified partuuid.
519 //
520 if (OcUnicodeStartsWith (Option->Unicode.Name, L"autoopts:", TRUE)) {
521 if (Option->Unicode.Value == NULL) {
522 DEBUG ((DEBUG_WARN, "LNX: Missing value for %s\n", Option->Unicode.Name));
523 continue;
524 }
525
527
528 Status = StrToGuid (&Option->Unicode.Name[L_STR_LEN (L"autoopts:")], &AutoOpts->Guid);
529 if (EFI_ERROR (Status)) {
530 DEBUG ((DEBUG_WARN, "LNX: Cannot parse partuuid from %s - %r\n", Option->Unicode.Name, Status));
532 continue;
533 }
534
535 AutoOpts->Opts = Option->Unicode.Value;
536 AutoOpts->PlusOpts = OcUnicodeEndsWith (Option->Unicode.Name, L"+", FALSE);
537 } else if (StrCmp (Option->Unicode.Name, L"autoopts") == 0) {
538 if (Option->Unicode.Value == NULL) {
539 DEBUG ((DEBUG_WARN, "LNX: Missing value for %s\n", Option->Unicode.Name));
540 continue;
541 }
542
543 mGlobalAutoOpts = Option->Unicode.Value;
544 } else if (StrCmp (Option->Unicode.Name, L"autoopts+") == 0) {
545 if (Option->Unicode.Value == NULL) {
546 DEBUG ((DEBUG_WARN, "LNX: Missing value for %s\n", Option->Unicode.Name));
547 continue;
548 }
549
551 }
552 }
553
554 return EFI_SUCCESS;
555}
556
557//
558// TODO: Options for rescue versions. Would it be better e.g. just to add "ro" and nothing else?
559// However on some installs (e.g. where modules to load are specified in the kernel opts) this
560// would not boot at all.
561// Maybe upgrade to autoopts:{partuuid}r="...": user options for rescue kernels on specified partuuid?
562//
563STATIC
564EFI_STATUS
566 IN CONST BOOLEAN IsRescue,
567 IN CONST BOOLEAN IsStandaloneBoot,
568 IN OC_FLEX_ARRAY *Options
569 )
570{
571 EFI_STATUS Status;
572 UINTN Index;
573 UINTN InsertIndex;
574 CHAR8 *AsciiStrValue;
575 CHAR8 *GrubVarName;
576 BOOLEAN FoundOptions;
577 CHAR8 *AddRxOption;
578
579 FoundOptions = FALSE;
580
581 //
582 // Do we have user-specified options for this partuuid?
583 //
585 DEBUG ((
586 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
587 "LNX: Using autoopts:%g%a=\"%s\"\n",
588 &gPartuuid,
589 "",
591 ));
593 return Status;
594 }
595
597 DEBUG ((
598 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
599 "LNX: Using autoopts:%g%a=\"%s\"\n",
600 &gPartuuid,
601 "+",
603 ));
605 if (EFI_ERROR (Status)) {
606 return Status;
607 }
608
609 FoundOptions = TRUE;
610 }
611
612 //
613 // Don't use autoopts if partition specific autoopts:{partuuid} already found.
614 //
615 if (!FoundOptions && mGlobalAutoOpts) {
616 DEBUG ((
617 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
618 "LNX: Using autoopts%a=\"%s\"\n",
619 "",
621 ));
622
624 return Status;
625 } else if (mGlobalAutoOptsPlus) {
626 DEBUG ((
627 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
628 "LNX: Using autoopts%a=\"%s\"\n",
629 "+",
631 ));
632
634 if (EFI_ERROR (Status)) {
635 return Status;
636 }
637
638 FoundOptions = TRUE;
639 }
640
641 //
642 // Should only have attempted to detect kernels on standalone boot partition if we had full user options.
643 //
644 ASSERT (!IsStandaloneBoot);
645
646 //
647 // Code only reaches here and below if has been nothing or only += options above.
648 // Use options from GRUB default location.
649 //
650 if (mEtcDefaultGrubOptions != NULL) {
651 //
652 // Insert these after "ro" but before any user specified opts.
653 //
654 InsertIndex = 0;
655
656 //
657 // If both are present both should be added, standard grub scripts add them
658 // in this order.
659 // Rescue should only use GRUB_CMDLINE_LINUX so this is correct as
660 // far as it goes; however note that rescue options are unfortunately not
661 // normally stored here, but are generated in the depths of grub scripts.
662 //
663 for (Index = 0; Index < (IsRescue ? 1u : 2u); Index++) {
664 if (Index == 0) {
665 GrubVarName = "GRUB_CMDLINE_LINUX";
666 } else {
667 GrubVarName = "GRUB_CMDLINE_LINUX_DEFAULT";
668 }
669
672 GrubVarName,
673 &AsciiStrValue
674 )
675 && (AsciiStrValue != NULL))
676 {
677 DEBUG ((
678 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
679 "LNX: Using %a=\"%a\"\n",
680 GrubVarName,
681 AsciiStrValue
682 ));
683
684 if (AsciiStrValue[0] != '\0') {
685 Status = InsertOption (InsertIndex, Options, AsciiStrValue, OcStringFormatAscii);
686 if (EFI_ERROR (Status)) {
687 return Status;
688 }
689
690 //
691 // Must not increment insert index if empty option.
692 //
693 InsertIndex++;
694 }
695
696 //
697 // Empty string value is good enough for found: we are operating
698 // from GRUB cfg files rather than pure guesswork.
699 //
700 FoundOptions = TRUE;
701 }
702 }
703 }
704
705 //
706 // It might be valid to have no options for some kernels or distros, but at least
707 // empty (not missing) user specified options or GRUB_CMDLINE_LINUX[_DEFAULT] needs
708 // to be present in that case or we stop.
709 //
710 if (!FoundOptions) {
711 DEBUG ((DEBUG_WARN, "LNX: No grub default or user defined options - aborting\n"));
712 return EFI_INVALID_PARAMETER;
713 }
714
715 #if !defined (LINUX_ALLOW_MBR)
716 if (CompareGuid (&gPartuuid, &gEfiPartTypeUnusedGuid)) {
717 Status = EFI_UNSUPPORTED;
718 DEBUG ((DEBUG_WARN, "LNX: Cannot autodetect root on MBR partition - %r\n", Status));
719 return Status;
720 }
721
722 #endif
723
724 //
725 // Insert "root=PARTUUID=..." option, followed by "ro" if requested, only if we get to here.
726 //
727 Status = InsertRootOption (Options);
728 if (EFI_ERROR (Status)) {
729 return Status;
730 }
731
732 InsertIndex = 1;
733
734 AddRxOption = NULL;
735 if ((gLinuxBootFlags & LINUX_BOOT_ADD_RW) != 0) {
736 AddRxOption = "rw";
737 } else if ((gLinuxBootFlags & LINUX_BOOT_ADD_RO) != 0) {
738 AddRxOption = "ro";
739 }
740
741 if (AddRxOption != NULL) {
742 DEBUG ((
743 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
744 "LNX: Adding \"%a\"\n",
745 AddRxOption
746 ));
747 Status = InsertOption (InsertIndex, Options, AddRxOption, OcStringFormatAscii);
748 }
749
750 return Status;
751}
752
753STATIC
754EFI_STATUS
756 IN CHAR16 *DirectoryPath,
757 IN CONST BOOLEAN IsStandaloneBoot
758 )
759{
760 EFI_STATUS Status;
761 UINTN VmlinuzIndex;
762 UINTN InitrdIndex;
763 UINTN ShortestMatch;
764 UINTN DirectoryPathLength;
765 LOADER_ENTRY *Entry;
766 VMLINUZ_FILE *VmlinuzFile;
767 VMLINUZ_FILE *InitrdFile;
768 VMLINUZ_FILE *InitrdMatch;
769 CHAR8 **Option;
770 BOOLEAN IsRescue;
771
772 ASSERT (DirectoryPath != NULL);
774
775 DirectoryPathLength = StrLen (DirectoryPath);
776
777 for (VmlinuzIndex = 0; VmlinuzIndex < mVmlinuzFiles->Count; VmlinuzIndex++) {
778 VmlinuzFile = OcFlexArrayItemAt (mVmlinuzFiles, VmlinuzIndex);
779
780 IsRescue = FALSE;
781 if ( OcUnicodeStartsWith (VmlinuzFile->Version, L"0", FALSE)
782 || (StrStr (VmlinuzFile->Version, L"rescue") != NULL))
783 {
784 //
785 // We might have to scan /boot/grb/grub.cfg as grub os-prober does if
786 // we want to find rescue version options, or we need to find a way
787 // for user to pass these in, since they are generated in the depths
788 // of the grub scripts, and in typical distros are not present in
789 // /etc/default/grub, even though it looks as if they could be.
790 //
791 IsRescue = TRUE;
792 DEBUG ((DEBUG_INFO, "LNX: %s=rescue\n", VmlinuzFile->Version));
793 }
794
795 ShortestMatch = MAX_UINTN;
796 InitrdMatch = NULL;
797
798 //
799 // Find shortest init* filename containing the same version string.
800 //
801 for (InitrdIndex = 0; InitrdIndex < mInitrdFiles->Count; InitrdIndex++) {
802 InitrdFile = OcFlexArrayItemAt (mInitrdFiles, InitrdIndex);
803 if (InitrdFile->StrLen < ShortestMatch) {
804 if (StrStr (InitrdFile->Version, VmlinuzFile->Version) != NULL) {
805 InitrdMatch = InitrdFile;
806 ShortestMatch = InitrdFile->StrLen;
807 }
808 }
809 }
810
812 if (Entry == NULL) {
813 return EFI_OUT_OF_RESOURCES;
814 }
815
816 //
817 // Linux.
818 //
819 Status = CreateAsciiRelativePath (
820 &Entry->Linux,
821 DirectoryPath,
822 DirectoryPathLength,
823 VmlinuzFile->FileName,
824 VmlinuzFile->StrLen
825 );
826 if (EFI_ERROR (Status)) {
827 return Status;
828 }
829
830 //
831 // Id and version from filename.
832 //
833 Status = InternalIdVersionFromFileName (Entry, VmlinuzFile->FileName);
834 if (EFI_ERROR (Status)) {
835 return Status;
836 }
837
838 //
839 // Use title from .contentDetails or os-release file.
840 //
841 if (mPrettyName != NULL) {
842 Entry->Title = AllocateCopyPool (AsciiStrSize (mPrettyName), mPrettyName);
843 if (Entry->Title == NULL) {
844 return EFI_OUT_OF_RESOURCES;
845 }
846 }
847
848 //
849 // Initrd.
850 //
851 if (InitrdMatch == NULL) {
852 //
853 // Note that where initrd was required user will see clear (and safe, i.e. will
854 // not just boot incorrectly) warning from Linux kernel as well.
855 //
856 DEBUG ((DEBUG_WARN, "LNX: No matching initrd/initramfs file found for %a\n", Entry->Linux));
857 } else {
858 Option = OcFlexArrayAddItem (Entry->Initrds);
859 if (Option == NULL) {
860 return EFI_OUT_OF_RESOURCES;
861 }
862
863 Status = CreateAsciiRelativePath (
864 Option,
865 DirectoryPath,
866 DirectoryPathLength,
867 InitrdMatch->FileName,
868 InitrdMatch->StrLen
869 );
870 if (EFI_ERROR (Status)) {
871 return Status;
872 }
873 }
874
875 //
876 // Add all options.
877 //
878 Status = AutodetectBootOptions (IsRescue, IsStandaloneBoot, Entry->Options);
879 if (EFI_ERROR (Status)) {
880 return Status;
881 }
882 }
883
884 return EFI_SUCCESS;
885}
886
887STATIC
888EFI_STATUS
890 IN EFI_FILE_PROTOCOL *RootDirectory,
891 IN EFI_FILE_PROTOCOL *VmlinuzDirectory,
892 IN CHAR16 *AutodetectDir,
893 IN CONST BOOLEAN IsStandaloneBoot,
894 OUT OC_PICKER_ENTRY **Entries,
895 OUT UINTN *NumEntries
896 )
897{
898 EFI_STATUS Status;
899 EFI_FILE_PROTOCOL *RootFsFile;
900
901 mVmlinuzFiles = NULL;
902 mInitrdFiles = NULL;
903
904 Status = EFI_SUCCESS;
905
906 if (!IsStandaloneBoot) {
907 Status = OcSafeFileOpen (RootDirectory, &RootFsFile, ROOT_FS_FILE, EFI_FILE_MODE_READ, 0);
908 if (!EFI_ERROR (Status)) {
909 Status = OcEnsureDirectoryFile (RootFsFile, FALSE);
910 if (EFI_ERROR (Status)) {
911 DEBUG ((DEBUG_WARN, "LNX: %s found but not a %a - %r\n", ROOT_FS_FILE, "file", Status));
912 }
913
914 RootFsFile->Close (RootFsFile);
915 }
916
917 if (EFI_ERROR (Status)) {
918 DEBUG ((DEBUG_WARN, "LNX: AutodetectLinux not root fs - %r\n", Status));
919 }
920 }
921
922 if (!EFI_ERROR (Status)) {
924 if (mVmlinuzFiles == NULL) {
925 Status = EFI_OUT_OF_RESOURCES;
926 }
927 }
928
929 if (!EFI_ERROR (Status)) {
931 if (mInitrdFiles == NULL) {
932 Status = EFI_OUT_OF_RESOURCES;
933 }
934 }
935
936 //
937 // Place vmlinuz* and init* files into arrays.
938 //
939 if (!EFI_ERROR (Status)) {
940 Status = OcScanDirectory (VmlinuzDirectory, ProcessVmlinuzFile, NULL);
941
942 if (!EFI_ERROR (Status) && (mVmlinuzFiles->Count == 0)) {
943 //
944 // If initrd files but no vmlinuz files are present in autodetect dir.
945 //
946 Status = EFI_NOT_FOUND;
947 }
948 }
949
950 if (!EFI_ERROR (Status)) {
952 sizeof (LOADER_ENTRY),
954 );
955 if (gLoaderEntries == NULL) {
956 Status = EFI_OUT_OF_RESOURCES;
957 } else {
958 LoadAppleDiskLabel (VmlinuzDirectory);
959 Status = LoadOsRelease (RootDirectory, IsStandaloneBoot);
960 if (!EFI_ERROR (Status)) {
961 Status = LoadDefaultGrub (RootDirectory, IsStandaloneBoot);
962 }
963
964 if (!EFI_ERROR (Status)) {
965 Status = GenerateEntriesForVmlinuzFiles (AutodetectDir, IsStandaloneBoot);
966 }
967
968 FreeEtcFiles ();
969 }
970
971 if (!EFI_ERROR (Status)) {
973 RootDirectory,
974 Entries,
975 NumEntries
976 );
977 }
978
980 }
981
982 if (mVmlinuzFiles != NULL) {
984 }
985
986 if (mInitrdFiles != NULL) {
988 }
989
990 return Status;
991}
992
993STATIC
994EFI_STATUS
996 IN EFI_FILE_PROTOCOL *RootDirectory,
997 IN EFI_FILE_PROTOCOL *VmlinuzDirectory,
998 IN CHAR16 *AutodetectDir,
999 IN CONST BOOLEAN IsStandaloneBoot,
1000 OUT OC_PICKER_ENTRY **Entries,
1001 OUT UINTN *NumEntries
1002 )
1003{
1004 EFI_STATUS Status;
1005
1006 Status = AutodetectLinuxAtDirectory (RootDirectory, VmlinuzDirectory, AutodetectDir, IsStandaloneBoot, Entries, NumEntries);
1007
1008 DEBUG ((
1009 (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) ? DEBUG_WARN : DEBUG_INFO,
1010 "LNX: AutodetectLinux %s - %r\n",
1011 AutodetectDir,
1012 Status
1013 ));
1014
1015 return Status;
1016}
1017
1018EFI_STATUS
1020 IN EFI_FILE_PROTOCOL *RootDirectory,
1021 OUT OC_PICKER_ENTRY **Entries,
1022 OUT UINTN *NumEntries
1023 )
1024{
1025 EFI_STATUS Status;
1026 EFI_FILE_PROTOCOL *VmlinuzDirectory;
1027
1029
1030 //
1031 // Autodetect at /boot if present.
1032 //
1033 Status = OcSafeFileOpen (RootDirectory, &VmlinuzDirectory, AUTODETECT_DIR, EFI_FILE_MODE_READ, 0);
1034 if (!EFI_ERROR (Status)) {
1035 Status = DoAutodetectLinux (RootDirectory, VmlinuzDirectory, AUTODETECT_DIR, FALSE, Entries, NumEntries);
1036 VmlinuzDirectory->Close (VmlinuzDirectory);
1037 }
1038
1039 //
1040 // Try to autodetect kernels at / only if detecting at /boot failed and we have full user-specified
1041 // options, since we can't autodetect any options on standalone /boot (unless we parse grub.cfg for
1042 // boot entries, and even then not all standalone boot setups have it).
1043 //
1044 if (EFI_ERROR (Status)) {
1045 if ((mCurrentPartuuidAutoOpts != NULL) || ((mGlobalAutoOpts != NULL) && (mCurrentPartuuidAutoOptsPlus == NULL))) {
1046 Status = DoAutodetectLinux (RootDirectory, RootDirectory, ROOT_DIR, TRUE, Entries, NumEntries);
1047 } else {
1048 DEBUG ((
1049 (gLinuxBootFlags & LINUX_BOOT_LOG_VERBOSE) == 0 ? DEBUG_VERBOSE : DEBUG_INFO,
1050 "LNX: Not trying to autodetect kernel on possible standalone /boot partition without full autoopts\n"
1051 ));
1052 }
1053 }
1054
1055 return Status;
1056}
UINT64 Length
STATIC OC_FLEX_ARRAY * mVmlinuzFiles
Definition Autodetect.c:33
STATIC EFI_STATUS CreateRootPartuuid(CHAR8 **Dest)
Definition Autodetect.c:171
STATIC CHAR16 * mGlobalAutoOpts
Definition Autodetect.c:77
STATIC OC_FLEX_ARRAY * mEtcOsReleaseOptions
Definition Autodetect.c:41
STATIC VOID LoadAppleDiskLabel(IN CONST EFI_FILE_PROTOCOL *VmlinuzDirectory)
Definition Autodetect.c:337
STATIC EFI_STATUS InsertOption(IN CONST UINTN InsertIndex, IN OC_FLEX_ARRAY *Options, IN CONST VOID *Value, IN CONST OC_STRING_FORMAT StringFormat)
Definition Autodetect.c:388
#define AUTODETECT_DIR
Definition Autodetect.c:27
STATIC CHAR16 * mCurrentPartuuidAutoOpts
Definition Autodetect.c:69
STATIC CHAR16 * mGlobalAutoOptsPlus
Definition Autodetect.c:81
STATIC OC_FLEX_ARRAY * mPerPartuuidAutoOpts
Definition Autodetect.c:65
#define ROOT_FS_FILE
Definition Autodetect.c:29
EFI_STATUS InsertRootOption(IN OC_FLEX_ARRAY *Options)
Definition Autodetect.c:444
#define ROOT_DIR
Definition Autodetect.c:28
EFI_STATUS InternalAutodetectLinux(IN EFI_FILE_PROTOCOL *RootDirectory, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
#define OS_RELEASE_FILE
Definition Autodetect.c:26
STATIC CHAR8 * mDiskLabel
Definition Autodetect.c:53
STATIC EFI_STATUS LoadDefaultGrub(IN CONST EFI_FILE_PROTOCOL *RootDirectory, IN CONST BOOLEAN IsStandaloneBoot)
Definition Autodetect.c:297
STATIC EFI_STATUS LoadOsRelease(IN CONST EFI_FILE_PROTOCOL *RootDirectory, IN CONST BOOLEAN IsStandaloneBoot)
Definition Autodetect.c:237
STATIC OC_FLEX_ARRAY * mEtcDefaultGrubOptions
Definition Autodetect.c:57
STATIC CHAR8 * mPrettyName
Definition Autodetect.c:49
STATIC CHAR8 * mEtcDefaultGrubFileContents
Definition Autodetect.c:61
STATIC OC_FLEX_ARRAY * mInitrdFiles
Definition Autodetect.c:37
STATIC CHAR8 * mEtcOsReleaseFileContents
Definition Autodetect.c:45
STATIC CHAR16 * mCurrentPartuuidAutoOptsPlus
Definition Autodetect.c:73
EFI_STATUS InternalPreloadAutoOpts(IN OC_FLEX_ARRAY *Options)
Definition Autodetect.c:489
STATIC EFI_STATUS DoAutodetectLinux(IN EFI_FILE_PROTOCOL *RootDirectory, IN EFI_FILE_PROTOCOL *VmlinuzDirectory, IN CHAR16 *AutodetectDir, IN CONST BOOLEAN IsStandaloneBoot, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
Definition Autodetect.c:995
STATIC EFI_STATUS AddOption(IN OC_FLEX_ARRAY *Options, IN CONST VOID *Value, IN CONST OC_STRING_FORMAT StringFormat)
Definition Autodetect.c:434
STATIC VOID GetCurrentPartuuidAutoOpts(VOID)
Definition Autodetect.c:466
STATIC VOID FreeEtcFiles(VOID)
Definition Autodetect.c:359
#define GRUB_DEFAULT_FILE
Definition Autodetect.c:25
STATIC EFI_STATUS ProcessVmlinuzFile(EFI_FILE_HANDLE Directory, EFI_FILE_INFO *FileInfo, UINTN FileInfoSize, VOID *Context OPTIONAL)
Definition Autodetect.c:85
STATIC EFI_STATUS CreateAsciiRelativePath(CHAR8 **Dest, CHAR16 *DirectoryPath, UINTN DirectoryPathLength, CHAR16 *FilePath, UINTN FilePathLength)
Definition Autodetect.c:144
STATIC VOID AutodetectTitle(VOID)
Definition Autodetect.c:198
STATIC EFI_STATUS AutodetectLinuxAtDirectory(IN EFI_FILE_PROTOCOL *RootDirectory, IN EFI_FILE_PROTOCOL *VmlinuzDirectory, IN CHAR16 *AutodetectDir, IN CONST BOOLEAN IsStandaloneBoot, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
Definition Autodetect.c:889
STATIC EFI_STATUS AutodetectBootOptions(IN CONST BOOLEAN IsRescue, IN CONST BOOLEAN IsStandaloneBoot, IN OC_FLEX_ARRAY *Options)
Definition Autodetect.c:565
STATIC EFI_STATUS GenerateEntriesForVmlinuzFiles(IN CHAR16 *DirectoryPath, IN CONST BOOLEAN IsStandaloneBoot)
Definition Autodetect.c:755
OC_FLEX_ARRAY * gLoaderEntries
EFI_GUID gPartuuid
#define LINUX_BOOT_ADD_RO
UINTN gLinuxBootFlags
EFI_STATUS InternalConvertLoaderEntriesToBootEntries(IN EFI_FILE_PROTOCOL *RootDirectory, OUT OC_PICKER_ENTRY **Entries, OUT UINTN *NumEntries)
#define LINUX_BOOT_ADD_RW
VOID InternalFreeLoaderEntry(LOADER_ENTRY *Entry)
OC_PICKER_CONTEXT * gPickerContext
LOADER_ENTRY * InternalAllocateLoaderEntry(VOID)
EFI_STATUS InternalIdVersionFromFileName(IN OUT LOADER_ENTRY *Entry, IN CHAR16 *FileName)
#define LINUX_BOOT_LOG_VERBOSE
DMG_FILEPATH_DEVICE_PATH FilePath
DMG_SIZE_DEVICE_PATH Size
BOOLEAN OcParsedVarsGetAsciiStr(IN CONST OC_FLEX_ARRAY *ParsedVars, IN CONST CHAR8 *Name, OUT CHAR8 **StrValue)
#define OC_ATTR_USE_DISK_LABEL_FILE
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)
VOID * OcFlexArrayInsertItem(IN OUT OC_FLEX_ARRAY *FlexArray, IN CONST UINTN InsertIndex)
Definition FlexArray.c:156
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)
VOID OcFlexArrayFreePointerItem(IN VOID *Item)
Definition FlexArray.c:18
VOID * OcFlexArrayAddItem(IN OUT OC_FLEX_ARRAY *FlexArray)
Definition FlexArray.c:136
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
CHAR8 * OcAsciiToLower(CHAR8 *Str)
Definition OcAsciiLib.c:555
CHAR16 *EFIAPI OcStrChr(IN CONST CHAR16 *String, IN CHAR16 Char)
#define L_STR_LEN(String)
Definition OcStringLib.h:26
@ OcStringFormatUnicode
Definition OcStringLib.h:51
@ OcStringFormatAscii
Definition OcStringLib.h:50
enum _OC_STRING_FORMAT OC_STRING_FORMAT
BOOLEAN EFIAPI OcUnicodeStartsWith(IN CONST CHAR16 *String, IN CONST CHAR16 *SearchString, IN BOOLEAN CaseInsensitiveMatch)
BOOLEAN EFIAPI OcUnicodeEndsWith(IN CONST CHAR16 *String, IN CONST CHAR16 *SearchString, IN BOOLEAN CaseInsensitiveMatch)
INTN EFIAPI CompareMem(IN CONST VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
BOOLEAN EFIAPI CompareGuid(IN CONST GUID *Guid1, IN CONST GUID *Guid2)
#define ASSERT(x)
Definition coder.h:55
OC_FLEX_ARRAY * Initrds
OC_FLEX_ARRAY * Options
OC_PARSED_VAR_UNICODE Unicode