]> code.delx.au - refind/blob - refind/main.c
Fine-tuning of loader detection code.
[refind] / refind / main.c
1 /*
2 * refind/main.c
3 * Main code for the boot menu
4 *
5 * Copyright (c) 2006-2010 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36 /*
37 * Modifications copyright (c) 2012 Roderick W. Smith
38 *
39 * Modifications distributed under the terms of the GNU General Public
40 * License (GPL) version 3 (GPLv3), a copy of which must be distributed
41 * with this source code or binaries made from it.
42 *
43 */
44
45 #include "global.h"
46 #include "config.h"
47 #include "screen.h"
48 #include "lib.h"
49 #include "icns.h"
50 #include "menu.h"
51 #include "mok.h"
52 #include "security_policy.h"
53 #include "../include/Handle.h"
54 #include "../include/refit_call_wrapper.h"
55 #include "driver_support.h"
56 #include "../include/syslinux_mbr.h"
57
58 #ifdef __MAKEWITH_GNUEFI
59 #define EFI_SECURITY_VIOLATION EFIERR (26)
60 #else
61 #include "../EfiLib/BdsHelper.h"
62 #endif // __MAKEWITH_GNUEFI
63
64 //
65 // variables
66
67 #define MACOSX_LOADER_PATH L"System\\Library\\CoreServices\\boot.efi"
68 #if defined (EFIX64)
69 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellx64.efi,\\shellx64.efi"
70 #define DRIVER_DIRS L"drivers,drivers_x64"
71 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootx64.efi"
72 #define FALLBACK_BASENAME L"bootx64.efi"
73 #elif defined (EFI32)
74 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi,\\EFI\\tools\\shellia32.efi,\\shellia32.efi"
75 #define DRIVER_DIRS L"drivers,drivers_ia32"
76 #define FALLBACK_FULLNAME L"EFI\\BOOT\\bootia32.efi"
77 #define FALLBACK_BASENAME L"bootia32.efi"
78 #else
79 #define SHELL_NAMES L"\\EFI\\tools\\shell.efi"
80 #define DRIVER_DIRS L"drivers"
81 #define FALLBACK_FULLNAME L"EFI\\BOOT\\boot.efi" /* Not really correct */
82 #define FALLBACK_BASENAME L"boot.efi" /* Not really correct */
83 #endif
84
85 #define MOK_NAMES L"\\EFI\\tools\\MokManager.efi,\\EFI\\fedora\\MokManager.efi,\\EFI\\redhat\\MokManager.efi,\\EFI\\ubuntu\\MokManager.efi,\\EFI\\suse\\MokManager"
86
87 // Filename patterns that identify EFI boot loaders. Note that a single case (either L"*.efi" or
88 // L"*.EFI") is fine for most systems; but Gigabyte's buggy Hybrid EFI does a case-sensitive
89 // comparison when it should do a case-insensitive comparison, so I'm doubling this up. It does
90 // no harm on other computers, AFAIK. In theory, every case variation should be done for
91 // completeness, but that's ridiculous....
92 #define LOADER_MATCH_PATTERNS L"*.efi,*.EFI"
93
94 // Patterns that identify Linux kernels. Added to the loader match pattern when the
95 // scan_all_linux_kernels option is set in the configuration file. Causes kernels WITHOUT
96 // a ".efi" extension to be found when scanning for boot loaders.
97 #define LINUX_MATCH_PATTERNS L"vmlinuz*,bzImage*"
98
99 // Default hint text for program-launch submenus
100 #define SUBSCREEN_HINT1 L"Use arrow keys to move cursor; Enter to boot;"
101 #define SUBSCREEN_HINT2 L"Insert or F2 to edit options; Esc to return to main menu"
102 #define SUBSCREEN_HINT2_NO_EDITOR L"Esc to return to main menu"
103
104 static REFIT_MENU_ENTRY MenuEntryAbout = { L"About rEFInd", TAG_ABOUT, 1, 0, 'A', NULL, NULL, NULL };
105 static REFIT_MENU_ENTRY MenuEntryReset = { L"Reboot Computer", TAG_REBOOT, 1, 0, 'R', NULL, NULL, NULL };
106 static REFIT_MENU_ENTRY MenuEntryShutdown = { L"Shut Down Computer", TAG_SHUTDOWN, 1, 0, 'U', NULL, NULL, NULL };
107 static REFIT_MENU_ENTRY MenuEntryReturn = { L"Return to Main Menu", TAG_RETURN, 0, 0, 0, NULL, NULL, NULL };
108 static REFIT_MENU_ENTRY MenuEntryExit = { L"Exit rEFInd", TAG_EXIT, 1, 0, 0, NULL, NULL, NULL };
109
110 static REFIT_MENU_SCREEN MainMenu = { L"Main Menu", NULL, 0, NULL, 0, NULL, 0, L"Automatic boot",
111 L"Use arrow keys to move cursor; Enter to boot;",
112 L"Insert or F2 for more options; Esc to refresh" };
113 static REFIT_MENU_SCREEN AboutMenu = { L"About", NULL, 0, NULL, 0, NULL, 0, NULL, L"Press Enter to return to main menu", L"" };
114
115 REFIT_CONFIG GlobalConfig = { FALSE, FALSE, 0, 0, 0, DONT_CHANGE_TEXT_MODE, 20, 0, 0, GRAPHICS_FOR_OSX, LEGACY_TYPE_MAC, 0,
116 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
117 {TAG_SHELL, TAG_APPLE_RECOVERY, TAG_MOK_TOOL, TAG_ABOUT, TAG_SHUTDOWN, TAG_REBOOT, 0, 0, 0, 0, 0 }};
118
119 // Structure used to hold boot loader filenames and time stamps in
120 // a linked list; used to sort entries within a directory.
121 struct LOADER_LIST {
122 CHAR16 *FileName;
123 EFI_TIME TimeStamp;
124 struct LOADER_LIST *NextEntry;
125 };
126
127 //
128 // misc functions
129 //
130
131 static VOID AboutrEFInd(VOID)
132 {
133 CHAR16 *TempStr; // Note: Don't deallocate; moved to menu structure
134
135 if (AboutMenu.EntryCount == 0) {
136 AboutMenu.TitleImage = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
137 AddMenuInfoLine(&AboutMenu, L"rEFInd Version 0.6.6.7");
138 AddMenuInfoLine(&AboutMenu, L"");
139 AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2006-2010 Christoph Pfisterer");
140 AddMenuInfoLine(&AboutMenu, L"Copyright (c) 2012 Roderick W. Smith");
141 AddMenuInfoLine(&AboutMenu, L"Portions Copyright (c) Intel Corporation and others");
142 AddMenuInfoLine(&AboutMenu, L"Distributed under the terms of the GNU GPLv3 license");
143 AddMenuInfoLine(&AboutMenu, L"");
144 AddMenuInfoLine(&AboutMenu, L"Running on:");
145 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
146 SPrint(TempStr, 255, L" EFI Revision %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & ((1 << 16) - 1));
147 AddMenuInfoLine(&AboutMenu, TempStr);
148 #if defined(EFI32)
149 AddMenuInfoLine(&AboutMenu, L" Platform: x86 (32 bit)");
150 #elif defined(EFIX64)
151 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
152 SPrint(TempStr, 255, L" Platform: x86_64 (64 bit); Secure Boot %s", secure_mode() ? L"active" : L"inactive");
153 AddMenuInfoLine(&AboutMenu, TempStr);
154 #else
155 AddMenuInfoLine(&AboutMenu, L" Platform: unknown");
156 #endif
157 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
158 SPrint(TempStr, 255, L" Firmware: %s %d.%02d",
159 ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & ((1 << 16) - 1));
160 AddMenuInfoLine(&AboutMenu, TempStr);
161 TempStr = AllocateZeroPool(256 * sizeof(CHAR16));
162 SPrint(TempStr, 255, L" Screen Output: %s", egScreenDescription());
163 AddMenuInfoLine(&AboutMenu, TempStr);
164 AddMenuInfoLine(&AboutMenu, L"");
165 #if defined(__MAKEWITH_GNUEFI)
166 AddMenuInfoLine(&AboutMenu, L"Built with GNU-EFI");
167 #else
168 AddMenuInfoLine(&AboutMenu, L"Built with TianoCore EDK2");
169 #endif
170 AddMenuInfoLine(&AboutMenu, L"");
171 AddMenuInfoLine(&AboutMenu, L"For more information, see the rEFInd Web site:");
172 AddMenuInfoLine(&AboutMenu, L"http://www.rodsbooks.com/refind/");
173 AddMenuEntry(&AboutMenu, &MenuEntryReturn);
174 }
175
176 RunMenu(&AboutMenu, NULL);
177 } /* VOID AboutrEFInd() */
178
179 // Launch an EFI binary.
180 static EFI_STATUS StartEFIImageList(IN EFI_DEVICE_PATH **DevicePaths,
181 IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
182 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
183 OUT UINTN *ErrorInStep,
184 IN BOOLEAN Verbose)
185 {
186 EFI_STATUS Status, ReturnStatus;
187 EFI_HANDLE ChildImageHandle;
188 EFI_LOADED_IMAGE *ChildLoadedImage = NULL;
189 UINTN DevicePathIndex;
190 CHAR16 ErrorInfo[256];
191 CHAR16 *FullLoadOptions = NULL;
192
193 if (ErrorInStep != NULL)
194 *ErrorInStep = 0;
195
196 // set load options
197 if (LoadOptions != NULL) {
198 if (LoadOptionsPrefix != NULL) {
199 MergeStrings(&FullLoadOptions, LoadOptions, L' ');
200 if (OSType == 'M') {
201 MergeStrings(&FullLoadOptions, L" ", 0);
202 // NOTE: That last space is also added by the EFI shell and seems to be significant
203 // when passing options to Apple's boot.efi...
204 } // if
205 } else {
206 MergeStrings(&FullLoadOptions, LoadOptions, 0);
207 } // if/else
208 } else { // LoadOptions == NULL
209 // NOTE: We provide a non-null string when no options are specified for safety;
210 // some systems (at least DUET) can hang when launching some programs (such as
211 // an EFI shell) without this.
212 FullLoadOptions = StrDuplicate(L" ");
213 }
214 if (Verbose)
215 Print(L"Starting %s\nUsing load options '%s'\n", ImageTitle, FullLoadOptions);
216
217 // load the image into memory (and execute it, in the case of a shim/MOK image).
218 ReturnStatus = Status = EFI_NOT_FOUND; // in case the list is empty
219 for (DevicePathIndex = 0; DevicePaths[DevicePathIndex] != NULL; DevicePathIndex++) {
220 // NOTE: Below commented-out line could be more efficient if file were read ahead of
221 // time and passed as a pre-loaded image to LoadImage(), but it doesn't work on my
222 // 32-bit Mac Mini or my 64-bit Intel box when launching a Linux kernel; the
223 // kernel returns a "Failed to handle fs_proto" error message.
224 // TODO: Track down the cause of this error and fix it, if possible.
225 // ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
226 // ImageData, ImageSize, &ChildImageHandle);
227 ReturnStatus = Status = refit_call6_wrapper(BS->LoadImage, FALSE, SelfImageHandle, DevicePaths[DevicePathIndex],
228 NULL, 0, &ChildImageHandle);
229 if (ReturnStatus != EFI_NOT_FOUND) {
230 break;
231 }
232 }
233 SPrint(ErrorInfo, 255, L"while loading %s", ImageTitle);
234 if (CheckError(Status, ErrorInfo)) {
235 if (ErrorInStep != NULL)
236 *ErrorInStep = 1;
237 goto bailout;
238 }
239
240 ReturnStatus = Status = refit_call3_wrapper(BS->HandleProtocol, ChildImageHandle, &LoadedImageProtocol,
241 (VOID **) &ChildLoadedImage);
242 if (CheckError(Status, L"while getting a LoadedImageProtocol handle")) {
243 if (ErrorInStep != NULL)
244 *ErrorInStep = 2;
245 goto bailout_unload;
246 }
247 ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions;
248 ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16);
249 // turn control over to the image
250 // TODO: (optionally) re-enable the EFI watchdog timer!
251
252 // close open file handles
253 UninitRefitLib();
254 ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL);
255
256 // control returns here when the child image calls Exit()
257 SPrint(ErrorInfo, 255, L"returned from %s", ImageTitle);
258 if (CheckError(Status, ErrorInfo)) {
259 if (ErrorInStep != NULL)
260 *ErrorInStep = 3;
261 }
262
263 // re-open file handles
264 ReinitRefitLib();
265
266 bailout_unload:
267 // unload the image, we don't care if it works or not...
268 Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle);
269
270 bailout:
271 MyFreePool(FullLoadOptions);
272 return ReturnStatus;
273 } /* static EFI_STATUS StartEFIImageList() */
274
275 static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
276 IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
277 IN CHAR16 *ImageTitle, IN CHAR8 OSType,
278 OUT UINTN *ErrorInStep,
279 IN BOOLEAN Verbose)
280 {
281 EFI_DEVICE_PATH *DevicePaths[2];
282
283 DevicePaths[0] = DevicePath;
284 DevicePaths[1] = NULL;
285 return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, OSType, ErrorInStep, Verbose);
286 } /* static EFI_STATUS StartEFIImage() */
287
288 //
289 // EFI OS loader functions
290 //
291
292 static VOID StartLoader(IN LOADER_ENTRY *Entry)
293 {
294 UINTN ErrorInStep = 0;
295
296 BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS");
297 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
298 Basename(Entry->LoaderPath), Entry->OSType, &ErrorInStep, !Entry->UseGraphicsMode);
299 FinishExternalScreen();
300 }
301
302 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
303 // The matching file has a name that begins with "init" and includes the same version
304 // number string as is found in LoaderPath -- but not a longer version number string.
305 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
306 // has a file called initramfs-3.3.0.img, this function will return the string
307 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
308 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
309 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
310 // finds). Thus, care should be taken to avoid placing duplicate matching files in
311 // the kernel's directory.
312 // If no matching init file can be found, returns NULL.
313 static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
314 CHAR16 *InitrdName = NULL, *FileName, *KernelVersion, *InitrdVersion, *Path;
315 REFIT_DIR_ITER DirIter;
316 EFI_FILE_INFO *DirEntry;
317
318 FileName = Basename(LoaderPath);
319 KernelVersion = FindNumbers(FileName);
320 Path = FindPath(LoaderPath);
321
322 // Add trailing backslash for root directory; necessary on some systems, but must
323 // NOT be added to all directories, since on other systems, a trailing backslash on
324 // anything but the root directory causes them to flake out!
325 if (StrLen(Path) == 0) {
326 MergeStrings(&Path, L"\\", 0);
327 } // if
328 DirIterOpen(Volume->RootDir, Path, &DirIter);
329 // Now add a trailing backslash if it was NOT added earlier, for consistency in
330 // building the InitrdName later....
331 if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\'))
332 MergeStrings(&Path, L"\\", 0);
333 while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) {
334 InitrdVersion = FindNumbers(DirEntry->FileName);
335 if (KernelVersion != NULL) {
336 if (StriCmp(InitrdVersion, KernelVersion) == 0) {
337 MergeStrings(&InitrdName, Path, 0);
338 MergeStrings(&InitrdName, DirEntry->FileName, 0);
339 } // if
340 } else {
341 if (InitrdVersion == NULL) {
342 MergeStrings(&InitrdName, Path, 0);
343 MergeStrings(&InitrdName, DirEntry->FileName, 0);
344 } // if
345 } // if/else
346 MyFreePool(InitrdVersion);
347 } // while
348 DirIterClose(&DirIter);
349
350 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
351 MyFreePool(KernelVersion);
352 MyFreePool(Path);
353 return (InitrdName);
354 } // static CHAR16 * FindInitrd()
355
356 LOADER_ENTRY * AddPreparedLoaderEntry(LOADER_ENTRY *Entry) {
357 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
358
359 return(Entry);
360 } // LOADER_ENTRY * AddPreparedLoaderEntry()
361
362 // Creates a copy of a menu screen.
363 // Returns a pointer to the copy of the menu screen.
364 static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) {
365 REFIT_MENU_SCREEN *NewEntry;
366 UINTN i;
367
368 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
369 if ((Entry != NULL) && (NewEntry != NULL)) {
370 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN));
371 NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL;
372 NewEntry->TimeoutText = (Entry->TimeoutText) ? StrDuplicate(Entry->TimeoutText) : NULL;
373 if (Entry->TitleImage != NULL) {
374 NewEntry->TitleImage = AllocatePool(sizeof(EG_IMAGE));
375 if (NewEntry->TitleImage != NULL)
376 CopyMem(NewEntry->TitleImage, Entry->TitleImage, sizeof(EG_IMAGE));
377 } // if
378 NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*)));
379 for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) {
380 NewEntry->InfoLines[i] = (Entry->InfoLines[i]) ? StrDuplicate(Entry->InfoLines[i]) : NULL;
381 } // for
382 NewEntry->Entries = (REFIT_MENU_ENTRY**) AllocateZeroPool(Entry->EntryCount * (sizeof (REFIT_MENU_ENTRY*)));
383 for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) {
384 AddMenuEntry(NewEntry, Entry->Entries[i]);
385 } // for
386 NewEntry->Hint1 = (Entry->Hint1) ? StrDuplicate(Entry->Hint1) : NULL;
387 NewEntry->Hint2 = (Entry->Hint2) ? StrDuplicate(Entry->Hint2) : NULL;
388 } // if
389 return (NewEntry);
390 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
391
392 // Creates a copy of a menu entry. Intended to enable moving a stack-based
393 // menu entry (such as the ones for the "reboot" and "exit" functions) to
394 // to the heap. This enables easier deletion of the whole set of menu
395 // entries when re-scanning.
396 // Returns a pointer to the copy of the menu entry.
397 static REFIT_MENU_ENTRY* CopyMenuEntry(REFIT_MENU_ENTRY *Entry) {
398 REFIT_MENU_ENTRY *NewEntry;
399
400 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY));
401 if ((Entry != NULL) && (NewEntry != NULL)) {
402 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_ENTRY));
403 NewEntry->Title = (Entry->Title) ? StrDuplicate(Entry->Title) : NULL;
404 if (Entry->BadgeImage != NULL) {
405 NewEntry->BadgeImage = AllocatePool(sizeof(EG_IMAGE));
406 if (NewEntry->BadgeImage != NULL)
407 CopyMem(NewEntry->BadgeImage, Entry->BadgeImage, sizeof(EG_IMAGE));
408 }
409 if (Entry->Image != NULL) {
410 NewEntry->Image = AllocatePool(sizeof(EG_IMAGE));
411 if (NewEntry->Image != NULL)
412 CopyMem(NewEntry->Image, Entry->Image, sizeof(EG_IMAGE));
413 }
414 if (Entry->SubScreen != NULL) {
415 NewEntry->SubScreen = CopyMenuScreen(Entry->SubScreen);
416 }
417 } // if
418 return (NewEntry);
419 } // REFIT_MENU_ENTRY* CopyMenuEntry()
420
421 // Creates a new LOADER_ENTRY data structure and populates it with
422 // default values from the specified Entry, or NULL values if Entry
423 // is unspecified (NULL).
424 // Returns a pointer to the new data structure, or NULL if it
425 // couldn't be allocated
426 LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) {
427 LOADER_ENTRY *NewEntry = NULL;
428
429 NewEntry = AllocateZeroPool(sizeof(LOADER_ENTRY));
430 if (NewEntry != NULL) {
431 NewEntry->me.Title = NULL;
432 NewEntry->me.Tag = TAG_LOADER;
433 NewEntry->Enabled = TRUE;
434 NewEntry->UseGraphicsMode = FALSE;
435 NewEntry->OSType = 0;
436 if (Entry != NULL) {
437 NewEntry->LoaderPath = (Entry->LoaderPath) ? StrDuplicate(Entry->LoaderPath) : NULL;
438 NewEntry->VolName = (Entry->VolName) ? StrDuplicate(Entry->VolName) : NULL;
439 NewEntry->DevicePath = Entry->DevicePath;
440 NewEntry->UseGraphicsMode = Entry->UseGraphicsMode;
441 NewEntry->LoadOptions = (Entry->LoadOptions) ? StrDuplicate(Entry->LoadOptions) : NULL;
442 NewEntry->InitrdPath = (Entry->InitrdPath) ? StrDuplicate(Entry->InitrdPath) : NULL;
443 }
444 } // if
445 return (NewEntry);
446 } // LOADER_ENTRY *InitializeLoaderEntry()
447
448 // Adds InitrdPath to Options, but only if Options doesn't already include an
449 // initrd= line. Done to enable overriding the default initrd selection in a
450 // refind_linux.conf file's options list.
451 // Returns a pointer to a new string. The calling function is responsible for
452 // freeing its memory.
453 static CHAR16 *AddInitrdToOptions(CHAR16 *Options, CHAR16 *InitrdPath) {
454 CHAR16 *NewOptions = NULL;
455
456 if (Options != NULL)
457 NewOptions = StrDuplicate(Options);
458 if ((InitrdPath != NULL) && !StriSubCmp(L"initrd=", Options)) {
459 MergeStrings(&NewOptions, L"initrd=", L' ');
460 MergeStrings(&NewOptions, InitrdPath, 0);
461 }
462 return NewOptions;
463 } // CHAR16 *AddInitrdToOptions()
464
465 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
466 // the default entry that launches the boot loader using the same options as the
467 // main Entry does. Subsequent options can be added by the calling function.
468 // If a subscreen already exists in the Entry that's passed to this function,
469 // it's left unchanged and a pointer to it is returned.
470 // Returns a pointer to the new subscreen data structure, or NULL if there
471 // were problems allocating memory.
472 REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) {
473 CHAR16 *FileName, *MainOptions = NULL;
474 REFIT_MENU_SCREEN *SubScreen = NULL;
475 LOADER_ENTRY *SubEntry;
476
477 FileName = Basename(Entry->LoaderPath);
478 if (Entry->me.SubScreen == NULL) { // No subscreen yet; initialize default entry....
479 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
480 if (SubScreen != NULL) {
481 SubScreen->Title = AllocateZeroPool(sizeof(CHAR16) * 256);
482 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s",
483 (Entry->Title != NULL) ? Entry->Title : FileName, Entry->VolName);
484 SubScreen->TitleImage = Entry->me.Image;
485 // default entry
486 SubEntry = InitializeLoaderEntry(Entry);
487 if (SubEntry != NULL) {
488 SubEntry->me.Title = StrDuplicate(L"Boot using default options");
489 MainOptions = SubEntry->LoadOptions;
490 SubEntry->LoadOptions = AddInitrdToOptions(MainOptions, SubEntry->InitrdPath);
491 MyFreePool(MainOptions);
492 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
493 } // if (SubEntry != NULL)
494 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
495 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
496 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
497 } else {
498 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
499 } // if/else
500 } // if (SubScreen != NULL)
501 } else { // existing subscreen; less initialization, and just add new entry later....
502 SubScreen = Entry->me.SubScreen;
503 } // if/else
504 return SubScreen;
505 } // REFIT_MENU_SCREEN *InitializeSubScreen()
506
507 VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
508 REFIT_MENU_SCREEN *SubScreen;
509 LOADER_ENTRY *SubEntry;
510 CHAR16 *InitrdName;
511 CHAR16 DiagsFileName[256];
512 REFIT_FILE *File;
513 UINTN TokenCount;
514 CHAR16 **TokenList;
515
516 // create the submenu
517 if (StrLen(Entry->Title) == 0) {
518 MyFreePool(Entry->Title);
519 Entry->Title = NULL;
520 }
521 SubScreen = InitializeSubScreen(Entry);
522
523 // loader-specific submenu entries
524 if (Entry->OSType == 'M') { // entries for Mac OS X
525 #if defined(EFIX64)
526 SubEntry = InitializeLoaderEntry(Entry);
527 if (SubEntry != NULL) {
528 SubEntry->me.Title = L"Boot Mac OS X with a 64-bit kernel";
529 SubEntry->LoadOptions = L"arch=x86_64";
530 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
531 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
532 } // if
533
534 SubEntry = InitializeLoaderEntry(Entry);
535 if (SubEntry != NULL) {
536 SubEntry->me.Title = L"Boot Mac OS X with a 32-bit kernel";
537 SubEntry->LoadOptions = L"arch=i386";
538 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
539 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
540 } // if
541 #endif
542
543 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SINGLEUSER)) {
544 SubEntry = InitializeLoaderEntry(Entry);
545 if (SubEntry != NULL) {
546 SubEntry->me.Title = L"Boot Mac OS X in verbose mode";
547 SubEntry->UseGraphicsMode = FALSE;
548 SubEntry->LoadOptions = L"-v";
549 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
550 } // if
551
552 #if defined(EFIX64)
553 SubEntry = InitializeLoaderEntry(Entry);
554 if (SubEntry != NULL) {
555 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (64-bit)";
556 SubEntry->UseGraphicsMode = FALSE;
557 SubEntry->LoadOptions = L"-v arch=x86_64";
558 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
559 }
560
561 SubEntry = InitializeLoaderEntry(Entry);
562 if (SubEntry != NULL) {
563 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (32-bit)";
564 SubEntry->UseGraphicsMode = FALSE;
565 SubEntry->LoadOptions = L"-v arch=i386";
566 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
567 }
568 #endif
569
570 SubEntry = InitializeLoaderEntry(Entry);
571 if (SubEntry != NULL) {
572 SubEntry->me.Title = L"Boot Mac OS X in single user mode";
573 SubEntry->UseGraphicsMode = FALSE;
574 SubEntry->LoadOptions = L"-v -s";
575 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
576 } // if
577 } // single-user mode allowed
578
579 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SAFEMODE)) {
580 SubEntry = InitializeLoaderEntry(Entry);
581 if (SubEntry != NULL) {
582 SubEntry->me.Title = L"Boot Mac OS X in safe mode";
583 SubEntry->UseGraphicsMode = FALSE;
584 SubEntry->LoadOptions = L"-v -x";
585 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
586 } // if
587 } // safe mode allowed
588
589 // check for Apple hardware diagnostics
590 StrCpy(DiagsFileName, L"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
591 if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) {
592 SubEntry = InitializeLoaderEntry(Entry);
593 if (SubEntry != NULL) {
594 SubEntry->me.Title = L"Run Apple Hardware Test";
595 MyFreePool(SubEntry->LoaderPath);
596 SubEntry->LoaderPath = StrDuplicate(DiagsFileName);
597 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
598 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
599 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
600 } // if
601 } // if diagnostics entry found
602
603 } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders
604 File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume);
605 if (File != NULL) {
606 InitrdName = FindInitrd(Entry->LoaderPath, Volume);
607 TokenCount = ReadTokenLine(File, &TokenList);
608 // first entry requires special processing, since it was initially set
609 // up with a default title but correct options by InitializeSubScreen(),
610 // earlier....
611 if ((SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) {
612 MyFreePool(SubScreen->Entries[0]->Title);
613 SubScreen->Entries[0]->Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
614 } // if
615 FreeTokenLine(&TokenList, &TokenCount);
616 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
617 SubEntry = InitializeLoaderEntry(Entry);
618 SubEntry->me.Title = TokenList[0] ? StrDuplicate(TokenList[0]) : StrDuplicate(L"Boot Linux");
619 MyFreePool(SubEntry->LoadOptions);
620 SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName);
621 FreeTokenLine(&TokenList, &TokenCount);
622 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
623 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
624 } // while
625 MyFreePool(InitrdName);
626 MyFreePool(File);
627 } // if Linux options file exists
628
629 } else if (Entry->OSType == 'E') { // entries for ELILO
630 SubEntry = InitializeLoaderEntry(Entry);
631 if (SubEntry != NULL) {
632 SubEntry->me.Title = L"Run ELILO in interactive mode";
633 SubEntry->LoadOptions = L"-p";
634 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
635 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
636 }
637
638 SubEntry = InitializeLoaderEntry(Entry);
639 if (SubEntry != NULL) {
640 SubEntry->me.Title = L"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
641 SubEntry->UseGraphicsMode = TRUE;
642 SubEntry->LoadOptions = L"-d 0 i17";
643 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
644 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
645 }
646
647 SubEntry = InitializeLoaderEntry(Entry);
648 if (SubEntry != NULL) {
649 SubEntry->me.Title = L"Boot Linux for a 20\" iMac (*)";
650 SubEntry->UseGraphicsMode = TRUE;
651 SubEntry->LoadOptions = L"-d 0 i20";
652 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
653 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
654 }
655
656 SubEntry = InitializeLoaderEntry(Entry);
657 if (SubEntry != NULL) {
658 SubEntry->me.Title = L"Boot Linux for a Mac Mini (*)";
659 SubEntry->UseGraphicsMode = TRUE;
660 SubEntry->LoadOptions = L"-d 0 mini";
661 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
662 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
663 }
664
665 AddMenuInfoLine(SubScreen, L"NOTE: This is an example. Entries");
666 AddMenuInfoLine(SubScreen, L"marked with (*) may not work.");
667
668 } else if (Entry->OSType == 'X') { // entries for xom.efi
669 // by default, skip the built-in selection and boot from hard disk only
670 Entry->LoadOptions = L"-s -h";
671
672 SubEntry = InitializeLoaderEntry(Entry);
673 if (SubEntry != NULL) {
674 SubEntry->me.Title = L"Boot Windows from Hard Disk";
675 SubEntry->LoadOptions = L"-s -h";
676 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
677 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
678 }
679
680 SubEntry = InitializeLoaderEntry(Entry);
681 if (SubEntry != NULL) {
682 SubEntry->me.Title = L"Boot Windows from CD-ROM";
683 SubEntry->LoadOptions = L"-s -c";
684 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
685 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
686 }
687
688 SubEntry = InitializeLoaderEntry(Entry);
689 if (SubEntry != NULL) {
690 SubEntry->me.Title = L"Run XOM in text mode";
691 SubEntry->UseGraphicsMode = FALSE;
692 SubEntry->LoadOptions = L"-v";
693 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
694 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
695 }
696 } // entries for xom.efi
697 AddMenuEntry(SubScreen, &MenuEntryReturn);
698 Entry->me.SubScreen = SubScreen;
699 } // VOID GenerateSubScreen()
700
701 // Returns options for a Linux kernel. Reads them from an options file in the
702 // kernel's directory; and if present, adds an initrd= option for an initial
703 // RAM disk file with the same version number as the kernel file.
704 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
705 CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL;
706
707 Options = GetFirstOptionsFromFile(LoaderPath, Volume);
708 InitrdName = FindInitrd(LoaderPath, Volume);
709 FullOptions = AddInitrdToOptions(Options, InitrdName);
710
711 MyFreePool(Options);
712 MyFreePool(InitrdName);
713 return (FullOptions);
714 } // static CHAR16 * GetMainLinuxOptions()
715
716 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
717 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
718 // that will (with luck) work fairly automatically.
719 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) {
720 CHAR16 *FileName, *PathOnly, *NoExtension, *OSIconName = NULL, *Temp, *SubString;
721 CHAR16 ShortcutLetter = 0;
722 UINTN i = 0, Length;
723
724 FileName = Basename(LoaderPath);
725 PathOnly = FindPath(LoaderPath);
726 NoExtension = StripEfiExtension(FileName);
727
728 // locate a custom icon for the loader
729 // Anything found here takes precedence over the "hints" in the OSIconName variable
730 if (!Entry->me.Image)
731 Entry->me.Image = egFindIcon(NoExtension, 128);
732 if (!Entry->me.Image)
733 Entry->me.Image = Volume->VolIconImage;
734
735 // Begin creating icon "hints" by using last part of directory path leading
736 // to the loader
737 Temp = FindLastDirName(LoaderPath);
738 MergeStrings(&OSIconName, Temp, L',');
739 MyFreePool(Temp);
740 Temp = NULL;
741 if (OSIconName != NULL) {
742 ShortcutLetter = OSIconName[0];
743 }
744
745 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
746 // underscores (_), to the list of hints to be used in searching for OS
747 // icons.
748 if ((Volume->VolName) && (StrLen(Volume->VolName) > 0)) {
749 Temp = SubString = StrDuplicate(Volume->VolName);
750 if (Temp != NULL) {
751 Length = StrLen(Temp);
752 for (i = 0; i < Length; i++) {
753 if ((Temp[i] == L' ') || (Temp[i] == L'_') || (Temp[i] == L'-')) {
754 Temp[i] = 0;
755 if (StrLen(SubString) > 0)
756 MergeStrings(&OSIconName, SubString, L',');
757 SubString = Temp + i + 1;
758 } // if
759 } // for
760 MergeStrings(&OSIconName, SubString, L',');
761 MyFreePool(Temp);
762 } // if
763 } // if
764
765 // detect specific loaders
766 if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) {
767 MergeStrings(&OSIconName, L"linux", L',');
768 Entry->OSType = 'L';
769 if (ShortcutLetter == 0)
770 ShortcutLetter = 'L';
771 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
772 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
773 } else if (StriSubCmp(L"refit", LoaderPath)) {
774 MergeStrings(&OSIconName, L"refit", L',');
775 Entry->OSType = 'R';
776 ShortcutLetter = 'R';
777 } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) {
778 if (Volume->VolIconImage != NULL) { // custom icon file found
779 Entry->me.Image = Volume->VolIconImage;
780 }
781 MergeStrings(&OSIconName, L"mac", L',');
782 Entry->OSType = 'M';
783 ShortcutLetter = 'M';
784 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
785 } else if (StriCmp(FileName, L"diags.efi") == 0) {
786 MergeStrings(&OSIconName, L"hwtest", L',');
787 } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0 || StriSubCmp(L"elilo", FileName)) {
788 MergeStrings(&OSIconName, L"elilo,linux", L',');
789 Entry->OSType = 'E';
790 if (ShortcutLetter == 0)
791 ShortcutLetter = 'L';
792 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
793 } else if (StriSubCmp(L"grub", FileName)) {
794 Entry->OSType = 'G';
795 ShortcutLetter = 'G';
796 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
797 } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
798 StriCmp(FileName, L"bootmgr.efi") == 0 ||
799 StriCmp(FileName, L"bootmgfw.efi") == 0) {
800 MergeStrings(&OSIconName, L"win", L',');
801 Entry->OSType = 'W';
802 ShortcutLetter = 'W';
803 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
804 } else if (StriCmp(FileName, L"xom.efi") == 0) {
805 MergeStrings(&OSIconName, L"xom,win", L',');
806 Entry->UseGraphicsMode = TRUE;
807 Entry->OSType = 'X';
808 ShortcutLetter = 'W';
809 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
810 }
811
812 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
813 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
814 Entry->me.ShortcutLetter = ShortcutLetter;
815 if (Entry->me.Image == NULL)
816 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
817 MyFreePool(PathOnly);
818 } // VOID SetLoaderDefaults()
819
820 // Add a specified EFI boot loader to the list, using automatic settings
821 // for icons, options, etc.
822 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
823 LOADER_ENTRY *Entry;
824
825 CleanUpPathNameSlashes(LoaderPath);
826 Entry = InitializeLoaderEntry(NULL);
827 if (Entry != NULL) {
828 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
829 Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
830 SPrint(Entry->me.Title, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
831 Entry->me.Row = 0;
832 Entry->me.BadgeImage = Volume->VolBadgeImage;
833 if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
834 Entry->LoaderPath = StrDuplicate(L"\\");
835 } else {
836 Entry->LoaderPath = NULL;
837 }
838 MergeStrings(&(Entry->LoaderPath), LoaderPath, 0);
839 Entry->VolName = Volume->VolName;
840 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
841 SetLoaderDefaults(Entry, LoaderPath, Volume);
842 GenerateSubScreen(Entry, Volume);
843 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
844 }
845
846 return(Entry);
847 } // LOADER_ENTRY * AddLoaderEntry()
848
849 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
850 // (Time1 == Time2). Precision is only to the nearest second; since
851 // this is used for sorting boot loader entries, differences smaller
852 // than this are likely to be meaningless (and unlikely!).
853 INTN TimeComp(IN EFI_TIME *Time1, IN EFI_TIME *Time2) {
854 INT64 Time1InSeconds, Time2InSeconds;
855
856 // Following values are overestimates; I'm assuming 31 days in every month.
857 // This is fine for the purpose of this function, which is limited
858 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
859 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
860 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
861 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
862 if (Time1InSeconds < Time2InSeconds)
863 return (-1);
864 else if (Time1InSeconds > Time2InSeconds)
865 return (1);
866
867 return 0;
868 } // INTN TimeComp()
869
870 // Adds a loader list element, keeping it sorted by date. Returns the new
871 // first element (the one with the most recent date).
872 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
873 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
874
875 LatestEntry = CurrentEntry = LoaderList;
876 if (LoaderList == NULL) {
877 LatestEntry = NewEntry;
878 } else {
879 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
880 PrevEntry = CurrentEntry;
881 CurrentEntry = CurrentEntry->NextEntry;
882 } // while
883 NewEntry->NextEntry = CurrentEntry;
884 if (PrevEntry == NULL) {
885 LatestEntry = NewEntry;
886 } else {
887 PrevEntry->NextEntry = NewEntry;
888 } // if/else
889 } // if/else
890 return (LatestEntry);
891 } // static VOID AddLoaderListEntry()
892
893 // Delete the LOADER_LIST linked list
894 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
895 struct LOADER_LIST *Temp;
896
897 while (LoaderList != NULL) {
898 Temp = LoaderList;
899 LoaderList = LoaderList->NextEntry;
900 MyFreePool(Temp->FileName);
901 MyFreePool(Temp);
902 } // while
903 } // static VOID CleanUpLoaderList()
904
905 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
906 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
907 // other than the one specified by Volume, or if the specified path is SelfDir.
908 // Returns TRUE if none of these conditions is met -- that is, if the path is
909 // eligible for scanning.
910 static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
911 CHAR16 *VolName = NULL, *DontScanDir;
912 UINTN i = 0, VolNum;
913 BOOLEAN ScanIt = TRUE;
914
915 if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes))
916 return FALSE;
917
918 if ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle == SelfVolume->DeviceHandle))
919 return FALSE;
920
921 while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) {
922 SplitVolumeAndFilename(&DontScanDir, &VolName);
923 CleanUpPathNameSlashes(DontScanDir);
924 if (VolName != NULL) {
925 if ((StriCmp(VolName, Volume->VolName) == 0) && (StriCmp(DontScanDir, Path) == 0))
926 ScanIt = FALSE;
927 if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) {
928 VolNum = Atoi(VolName + 2);
929 if ((VolNum == Volume->VolNumber) && (StriCmp(DontScanDir, Path) == 0))
930 ScanIt = FALSE;
931 }
932 } else {
933 if (StriCmp(DontScanDir, Path) == 0)
934 ScanIt = FALSE;
935 }
936 MyFreePool(DontScanDir);
937 DontScanDir = NULL;
938 }
939 return ScanIt;
940 } // BOOLEAN ShouldScan()
941
942 // Returns TRUE if the file is byte-for-byte identical with the fallback file
943 // on the volume AND if the file is not itself the fallback file; returns
944 // FALSE if the file is not identical to the fallback file OR if the file
945 // IS the fallback file. Intended for use in excluding the fallback boot
946 // loader when it's a duplicate of another boot loader.
947 static BOOLEAN DuplicatesFallback(IN REFIT_VOLUME *Volume, IN CHAR16 *FileName) {
948 CHAR8 *FileContents, *FallbackContents;
949 EFI_FILE_HANDLE FileHandle, FallbackHandle;
950 EFI_FILE_INFO *FileInfo, *FallbackInfo;
951 UINTN FileSize = 0, FallbackSize = 0;
952 EFI_STATUS Status;
953 BOOLEAN AreIdentical = FALSE;
954
955 CleanUpPathNameSlashes(FileName);
956
957 if (StriCmp(FileName, FALLBACK_FULLNAME) == 0)
958 return FALSE; // identical filenames, so not a duplicate....
959
960 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
961 if (Status == EFI_SUCCESS) {
962 FileInfo = LibFileInfo(FileHandle);
963 FileSize = FileInfo->FileSize;
964 } else {
965 return FALSE;
966 }
967
968 Status = refit_call5_wrapper(Volume->RootDir->Open, Volume->RootDir, &FallbackHandle, FALLBACK_FULLNAME, EFI_FILE_MODE_READ, 0);
969 if (Status == EFI_SUCCESS) {
970 FallbackInfo = LibFileInfo(FallbackHandle);
971 FallbackSize = FallbackInfo->FileSize;
972 } else {
973 refit_call1_wrapper(FileHandle->Close, FileHandle);
974 return FALSE;
975 }
976
977 if (FallbackSize != FileSize) { // not same size, so can't be identical
978 AreIdentical = FALSE;
979 } else { // could be identical; do full check....
980 FileContents = AllocatePool(FileSize);
981 FallbackContents = AllocatePool(FallbackSize);
982 if (FileContents && FallbackContents) {
983 Status = refit_call3_wrapper(FileHandle->Read, FileHandle, &FileSize, FileContents);
984 if (Status == EFI_SUCCESS)
985 Status = refit_call3_wrapper(FallbackHandle->Read, FallbackHandle, &FallbackSize, FallbackContents);
986 if (Status == EFI_SUCCESS) {
987 AreIdentical = (CompareMem(FileContents, FallbackContents, FileSize) == 0);
988 } // if
989 } // if
990 MyFreePool(FileContents);
991 MyFreePool(FallbackContents);
992 } // if/else
993
994 refit_call1_wrapper(FileHandle->Close, FileHandle);
995 refit_call1_wrapper(FileHandle->Close, FallbackHandle);
996 return AreIdentical;
997
998 } // BOOLEAN DuplicatesFallback()
999
1000 // Scan an individual directory for EFI boot loader files and, if found,
1001 // add them to the list. Exception: Ignores FALLBACK_FULLNAME, which is picked
1002 // up in ScanEfiFiles(). Sorts the entries within the loader directory so that
1003 // the most recent one appears first in the list.
1004 // Returns TRUE if a duplicate for FALLBACK_FILENAME was found, FALSE if not.
1005 static BOOLEAN ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
1006 {
1007 EFI_STATUS Status;
1008 REFIT_DIR_ITER DirIter;
1009 EFI_FILE_INFO *DirEntry;
1010 CHAR16 FileName[256], *Extension;
1011 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
1012 BOOLEAN FoundFallbackDuplicate = FALSE;
1013
1014 if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
1015 (StriCmp(Path, SelfDirPath) != 0)) &&
1016 (ShouldScan(Volume, Path))) {
1017 // look through contents of the directory
1018 DirIterOpen(Volume->RootDir, Path, &DirIter);
1019 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
1020 Extension = FindExtension(DirEntry->FileName);
1021 if (DirEntry->FileName[0] == '.' ||
1022 StriCmp(Extension, L".icns") == 0 ||
1023 StriCmp(Extension, L".png") == 0 ||
1024 (StriCmp(DirEntry->FileName, FALLBACK_BASENAME) == 0 && (StriCmp(Path, L"EFI\\BOOT") == 0)) ||
1025 StriSubCmp(L"shell", DirEntry->FileName) ||
1026 IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles))
1027 continue; // skip this
1028
1029 if (Path)
1030 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
1031 else
1032 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
1033 CleanUpPathNameSlashes(FileName);
1034 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
1035 if (NewLoader != NULL) {
1036 NewLoader->FileName = StrDuplicate(FileName);
1037 NewLoader->TimeStamp = DirEntry->ModificationTime;
1038 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
1039 if (DuplicatesFallback(Volume, FileName))
1040 FoundFallbackDuplicate = TRUE;
1041 } // if
1042 MyFreePool(Extension);
1043 } // while
1044 NewLoader = LoaderList;
1045 while (NewLoader != NULL) {
1046 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
1047 NewLoader = NewLoader->NextEntry;
1048 } // while
1049 CleanUpLoaderList(LoaderList);
1050 Status = DirIterClose(&DirIter);
1051 if (Status != EFI_NOT_FOUND) {
1052 if (Path)
1053 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1054 else
1055 StrCpy(FileName, L"while scanning the root directory");
1056 CheckError(Status, FileName);
1057 } // if (Status != EFI_NOT_FOUND)
1058 } // if not scanning our own directory
1059
1060 return FoundFallbackDuplicate;
1061 } /* static VOID ScanLoaderDir() */
1062
1063 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
1064 EFI_STATUS Status;
1065 REFIT_DIR_ITER EfiDirIter;
1066 EFI_FILE_INFO *EfiDirEntry;
1067 CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL;
1068 UINTN i, Length;
1069 BOOLEAN ScanFallbackLoader = TRUE;
1070
1071 // Print(L"Entering ScanEfiFiles(), GlobalConfig.ScanAllLinux = %s\n", GlobalConfig.ScanAllLinux ? L"TRUE" : L"FALSE");
1072 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
1073 if (GlobalConfig.ScanAllLinux)
1074 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
1075 // Print(L"MatchPatterns = '%s'\n", MatchPatterns);
1076
1077 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
1078 // check for Mac OS X boot loader
1079 if (ShouldScan(Volume, L"System\\Library\\CoreServices")) {
1080 StrCpy(FileName, MACOSX_LOADER_PATH);
1081 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1082 AddLoaderEntry(FileName, L"Mac OS X", Volume);
1083 if (DuplicatesFallback(Volume, FileName))
1084 ScanFallbackLoader = FALSE;
1085 }
1086
1087 // check for XOM
1088 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
1089 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1090 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
1091 if (DuplicatesFallback(Volume, FileName))
1092 ScanFallbackLoader = FALSE;
1093 }
1094 } // if should scan Mac directory
1095
1096 // check for Microsoft boot loader/menu
1097 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1098 if (FileExists(Volume->RootDir, FileName) && ShouldScan(Volume, L"EFI\\Microsoft\\Boot") &&
1099 !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
1100 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
1101 if (DuplicatesFallback(Volume, FileName))
1102 ScanFallbackLoader = FALSE;
1103 }
1104
1105 // scan the root directory for EFI executables
1106 if (ScanLoaderDir(Volume, L"\\", MatchPatterns))
1107 ScanFallbackLoader = FALSE;
1108
1109 // scan subdirectories of the EFI directory (as per the standard)
1110 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
1111 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
1112 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
1113 continue; // skip this, doesn't contain boot loaders or is scanned later
1114 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
1115 if (ScanLoaderDir(Volume, FileName, MatchPatterns))
1116 ScanFallbackLoader = FALSE;
1117 } // while()
1118 Status = DirIterClose(&EfiDirIter);
1119 if (Status != EFI_NOT_FOUND)
1120 CheckError(Status, L"while scanning the EFI directory");
1121
1122 // Scan user-specified (or additional default) directories....
1123 i = 0;
1124 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
1125 SplitVolumeAndFilename(&Directory, &VolName);
1126 CleanUpPathNameSlashes(Directory);
1127 Length = StrLen(Directory);
1128 if ((Length > 0) && ScanLoaderDir(Volume, Directory, MatchPatterns))
1129 ScanFallbackLoader = FALSE;
1130 MyFreePool(Directory);
1131 MyFreePool(VolName);
1132 } // while
1133
1134 // If not a duplicate & if it exists & if it's not us, create an entry
1135 // for the fallback boot loader
1136 if (ScanFallbackLoader && FileExists(Volume->RootDir, FALLBACK_FULLNAME) && ShouldScan(Volume, L"EFI\\BOOT")) {
1137 AddLoaderEntry(FALLBACK_FULLNAME, L"Fallback boot loader", Volume);
1138 }
1139 } // if
1140 } // static VOID ScanEfiFiles()
1141
1142 // Scan internal disks for valid EFI boot loaders....
1143 static VOID ScanInternal(VOID) {
1144 UINTN VolumeIndex;
1145
1146 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1147 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
1148 ScanEfiFiles(Volumes[VolumeIndex]);
1149 }
1150 } // for
1151 } // static VOID ScanInternal()
1152
1153 // Scan external disks for valid EFI boot loaders....
1154 static VOID ScanExternal(VOID) {
1155 UINTN VolumeIndex;
1156
1157 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1158 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
1159 ScanEfiFiles(Volumes[VolumeIndex]);
1160 }
1161 } // for
1162 } // static VOID ScanExternal()
1163
1164 // Scan internal disks for valid EFI boot loaders....
1165 static VOID ScanOptical(VOID) {
1166 UINTN VolumeIndex;
1167
1168 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1169 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
1170 ScanEfiFiles(Volumes[VolumeIndex]);
1171 }
1172 } // for
1173 } // static VOID ScanOptical()
1174
1175 //
1176 // legacy boot functions
1177 //
1178
1179 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
1180 {
1181 EFI_STATUS Status;
1182 UINT8 SectorBuffer[512];
1183 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
1184 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1185 UINTN LogicalPartitionIndex = 4;
1186 UINTN i;
1187 BOOLEAN HaveBootCode;
1188
1189 // read MBR
1190 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1191 if (EFI_ERROR(Status))
1192 return Status;
1193 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1194 return EFI_NOT_FOUND; // safety measure #1
1195
1196 // add boot code if necessary
1197 HaveBootCode = FALSE;
1198 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
1199 if (SectorBuffer[i] != 0) {
1200 HaveBootCode = TRUE;
1201 break;
1202 }
1203 }
1204 if (!HaveBootCode) {
1205 // no boot code found in the MBR, add the syslinux MBR code
1206 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
1207 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
1208 }
1209
1210 // set the partition active
1211 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1212 ExtBase = 0;
1213 for (i = 0; i < 4; i++) {
1214 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1215 return EFI_NOT_FOUND; // safety measure #2
1216 if (i == PartitionIndex)
1217 MbrTable[i].Flags = 0x80;
1218 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1219 MbrTable[i].Flags = 0x80;
1220 ExtBase = MbrTable[i].StartLBA;
1221 } else
1222 MbrTable[i].Flags = 0x00;
1223 }
1224
1225 // write MBR
1226 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1227 if (EFI_ERROR(Status))
1228 return Status;
1229
1230 if (PartitionIndex >= 4) {
1231 // we have to activate a logical partition, so walk the EMBR chain
1232
1233 // NOTE: ExtBase was set above while looking at the MBR table
1234 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1235 // read current EMBR
1236 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1237 if (EFI_ERROR(Status))
1238 return Status;
1239 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1240 return EFI_NOT_FOUND; // safety measure #3
1241
1242 // scan EMBR, set appropriate partition active
1243 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1244 NextExtCurrent = 0;
1245 for (i = 0; i < 4; i++) {
1246 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1247 return EFI_NOT_FOUND; // safety measure #4
1248 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1249 break;
1250 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1251 // link to next EMBR
1252 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1253 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1254 break;
1255 } else {
1256 // logical partition
1257 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1258 LogicalPartitionIndex++;
1259 }
1260 }
1261
1262 // write current EMBR
1263 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1264 if (EFI_ERROR(Status))
1265 return Status;
1266
1267 if (PartitionIndex < LogicalPartitionIndex)
1268 break; // stop the loop, no need to touch further EMBRs
1269 }
1270
1271 }
1272
1273 return EFI_SUCCESS;
1274 } /* static EFI_STATUS ActivateMbrPartition() */
1275
1276 // early 2006 Core Duo / Core Solo models
1277 static UINT8 LegacyLoaderDevicePath1Data[] = {
1278 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1279 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1280 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1281 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1282 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1283 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1284 };
1285 // mid-2006 Mac Pro (and probably other Core 2 models)
1286 static UINT8 LegacyLoaderDevicePath2Data[] = {
1287 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1288 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1289 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1290 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1291 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1292 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1293 };
1294 // mid-2007 MBP ("Santa Rosa" based models)
1295 static UINT8 LegacyLoaderDevicePath3Data[] = {
1296 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1297 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1298 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1299 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1300 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1301 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1302 };
1303 // early-2008 MBA
1304 static UINT8 LegacyLoaderDevicePath4Data[] = {
1305 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1306 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1307 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1308 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1309 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1310 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1311 };
1312 // late-2008 MB/MBP (NVidia chipset)
1313 static UINT8 LegacyLoaderDevicePath5Data[] = {
1314 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1315 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1316 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1317 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1318 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1319 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1320 };
1321
1322 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1323 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1324 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1325 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1326 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1327 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1328 NULL
1329 };
1330
1331 #define MAX_DISCOVERED_PATHS (16)
1332
1333 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1334 {
1335 EFI_STATUS Status;
1336 EG_IMAGE *BootLogoImage;
1337 UINTN ErrorInStep = 0;
1338 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1339
1340 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
1341
1342 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1343 if (BootLogoImage != NULL)
1344 BltImageAlpha(BootLogoImage,
1345 (UGAWidth - BootLogoImage->Width ) >> 1,
1346 (UGAHeight - BootLogoImage->Height) >> 1,
1347 &StdBackgroundPixel);
1348
1349 if (Entry->Volume->IsMbrPartition) {
1350 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1351 }
1352
1353 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1354
1355 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", 0, &ErrorInStep, TRUE);
1356 if (Status == EFI_NOT_FOUND) {
1357 if (ErrorInStep == 1) {
1358 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1359 } else if (ErrorInStep == 3) {
1360 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1361 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1362 }
1363 }
1364 FinishExternalScreen();
1365 } /* static VOID StartLegacy() */
1366
1367 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1368 #ifdef __MAKEWITH_TIANO
1369 static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry)
1370 {
1371 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1372
1373 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1374 BdsLibDoLegacyBoot(Entry->BdsOption);
1375
1376 // If we get here, it means that there was a failure....
1377 Print(L"Failure booting legacy (BIOS) OS.");
1378 PauseForKey();
1379 FinishExternalScreen();
1380 } // static VOID StartLegacyUEFI()
1381 #endif // __MAKEWITH_TIANO
1382
1383 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1384 {
1385 LEGACY_ENTRY *Entry, *SubEntry;
1386 REFIT_MENU_SCREEN *SubScreen;
1387 CHAR16 *VolDesc;
1388 CHAR16 ShortcutLetter = 0;
1389
1390 if (LoaderTitle == NULL) {
1391 if (Volume->OSName != NULL) {
1392 LoaderTitle = Volume->OSName;
1393 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1394 ShortcutLetter = LoaderTitle[0];
1395 } else
1396 LoaderTitle = L"Legacy OS";
1397 }
1398 if (Volume->VolName != NULL)
1399 VolDesc = Volume->VolName;
1400 else
1401 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1402
1403 // prepare the menu entry
1404 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1405 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1406 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1407 Entry->me.Tag = TAG_LEGACY;
1408 Entry->me.Row = 0;
1409 Entry->me.ShortcutLetter = ShortcutLetter;
1410 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1411 Entry->me.BadgeImage = Volume->VolBadgeImage;
1412 Entry->Volume = Volume;
1413 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1414 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1415 Entry->Enabled = TRUE;
1416
1417 // create the submenu
1418 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1419 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1420 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1421 SubScreen->TitleImage = Entry->me.Image;
1422 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1423 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1424 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1425 } else {
1426 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1427 } // if/else
1428
1429 // default entry
1430 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1431 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1432 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1433 SubEntry->me.Tag = TAG_LEGACY;
1434 SubEntry->Volume = Entry->Volume;
1435 SubEntry->LoadOptions = Entry->LoadOptions;
1436 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1437
1438 AddMenuEntry(SubScreen, &MenuEntryReturn);
1439 Entry->me.SubScreen = SubScreen;
1440 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1441 return Entry;
1442 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1443
1444
1445 #ifdef __MAKEWITH_GNUEFI
1446 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1447 #else
1448 // default volume badge icon based on disk kind
1449 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1450 EG_IMAGE * Badge = NULL;
1451
1452 switch (DiskType) {
1453 case BBS_HARDDISK:
1454 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1455 break;
1456 case BBS_USB:
1457 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1458 break;
1459 case BBS_CDROM:
1460 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1461 break;
1462 } // switch()
1463 return Badge;
1464 } // static EG_IMAGE * GetDiskBadge()
1465
1466 /**
1467 Create a rEFInd boot option from a Legacy BIOS protocol option.
1468 */
1469 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1470 {
1471 LEGACY_ENTRY *Entry, *SubEntry;
1472 REFIT_MENU_SCREEN *SubScreen;
1473 CHAR16 ShortcutLetter = 0;
1474 CHAR16 *LegacyDescription = BdsOption->Description;
1475
1476 // prepare the menu entry
1477 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1478 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1479 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1480 Entry->me.Tag = TAG_LEGACY_UEFI;
1481 Entry->me.Row = 0;
1482 Entry->me.ShortcutLetter = ShortcutLetter;
1483 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1484 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1485 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1486 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1487 Entry->BdsOption = BdsOption;
1488 Entry->Enabled = TRUE;
1489
1490 // create the submenu
1491 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1492 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1493 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1494 SubScreen->TitleImage = Entry->me.Image;
1495 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1496 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1497 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1498 } else {
1499 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1500 } // if/else
1501
1502 // default entry
1503 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1504 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1505 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1506 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1507 Entry->BdsOption = BdsOption;
1508 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1509
1510 AddMenuEntry(SubScreen, &MenuEntryReturn);
1511 Entry->me.SubScreen = SubScreen;
1512 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1513 return Entry;
1514 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1515
1516 /**
1517 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1518 In testing, protocol has not been implemented on Macs but has been
1519 implemented on several Dell PCs and an ASUS motherboard.
1520 Restricts output to disks of the specified DiskType.
1521 */
1522 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1523 {
1524 EFI_STATUS Status;
1525 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1526 UINT16 *BootOrder = NULL;
1527 UINTN Index = 0;
1528 CHAR16 BootOption[10];
1529 UINTN BootOrderSize = 0;
1530 CHAR16 Buffer[20];
1531 BDS_COMMON_OPTION *BdsOption;
1532 LIST_ENTRY TempList;
1533 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1534
1535 InitializeListHead (&TempList);
1536 ZeroMem (Buffer, sizeof (Buffer));
1537
1538 // If LegacyBios protocol is not implemented on this platform, then
1539 //we do not support this type of legacy boot on this machine.
1540 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1541 if (EFI_ERROR (Status))
1542 return;
1543
1544 // Grab the boot order
1545 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1546 if (BootOrder == NULL) {
1547 BootOrderSize = 0;
1548 }
1549
1550 Index = 0;
1551 while (Index < BootOrderSize / sizeof (UINT16))
1552 {
1553 // Grab each boot option variable from the boot order, and convert
1554 // the variable into a BDS boot option
1555 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1556 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1557
1558 if (BdsOption != NULL) {
1559 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1560
1561 // Only add the entry if it is of a requested type (e.g. USB, HD)
1562
1563 // Two checks necessary because some systems return EFI boot loaders
1564 // with a DeviceType value that would inappropriately include them
1565 // as legacy loaders....
1566 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1567 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1568 }
1569 }
1570 Index++;
1571 }
1572 } /* static VOID ScanLegacyUEFI() */
1573 #endif // __MAKEWITH_GNUEFI
1574
1575 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1576 UINTN VolumeIndex2;
1577 BOOLEAN ShowVolume, HideIfOthersFound;
1578
1579 ShowVolume = FALSE;
1580 HideIfOthersFound = FALSE;
1581 if (Volume->IsAppleLegacy) {
1582 ShowVolume = TRUE;
1583 HideIfOthersFound = TRUE;
1584 } else if (Volume->HasBootCode) {
1585 ShowVolume = TRUE;
1586 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1587 Volume->BlockIOOffset == 0 &&
1588 Volume->OSName == NULL)
1589 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1590 HideIfOthersFound = TRUE;
1591 }
1592 if (HideIfOthersFound) {
1593 // check for other bootable entries on the same disk
1594 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1595 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1596 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1597 ShowVolume = FALSE;
1598 }
1599 }
1600
1601 if (ShowVolume)
1602 AddLegacyEntry(NULL, Volume);
1603 } // static VOID ScanLegacyVolume()
1604
1605 // Scan attached optical discs for legacy (BIOS) boot code
1606 // and add anything found to the list....
1607 static VOID ScanLegacyDisc(VOID)
1608 {
1609 UINTN VolumeIndex;
1610 REFIT_VOLUME *Volume;
1611
1612 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1613 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1614 Volume = Volumes[VolumeIndex];
1615 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1616 ScanLegacyVolume(Volume, VolumeIndex);
1617 } // for
1618 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1619 ScanLegacyUEFI(BBS_CDROM);
1620 }
1621 } /* static VOID ScanLegacyDisc() */
1622
1623 // Scan internal hard disks for legacy (BIOS) boot code
1624 // and add anything found to the list....
1625 static VOID ScanLegacyInternal(VOID)
1626 {
1627 UINTN VolumeIndex;
1628 REFIT_VOLUME *Volume;
1629
1630 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1631 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1632 Volume = Volumes[VolumeIndex];
1633 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1634 ScanLegacyVolume(Volume, VolumeIndex);
1635 } // for
1636 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1637 ScanLegacyUEFI(BBS_HARDDISK);
1638 }
1639 } /* static VOID ScanLegacyInternal() */
1640
1641 // Scan external disks for legacy (BIOS) boot code
1642 // and add anything found to the list....
1643 static VOID ScanLegacyExternal(VOID)
1644 {
1645 UINTN VolumeIndex;
1646 REFIT_VOLUME *Volume;
1647
1648 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1649 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1650 Volume = Volumes[VolumeIndex];
1651 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1652 ScanLegacyVolume(Volume, VolumeIndex);
1653 } // for
1654 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1655 ScanLegacyUEFI(BBS_USB);
1656 }
1657 } /* static VOID ScanLegacyExternal() */
1658
1659 //
1660 // pre-boot tool functions
1661 //
1662
1663 static VOID StartTool(IN LOADER_ENTRY *Entry)
1664 {
1665 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1666 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1667 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1668 FinishExternalScreen();
1669 } /* static VOID StartTool() */
1670
1671 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1672 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1673 {
1674 LOADER_ENTRY *Entry;
1675 CHAR16 *TitleStr = NULL;
1676
1677 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1678
1679 MergeStrings(&TitleStr, L"Start ", 0);
1680 MergeStrings(&TitleStr, LoaderTitle, 0);
1681 Entry->me.Title = TitleStr;
1682 Entry->me.Tag = TAG_TOOL;
1683 Entry->me.Row = 1;
1684 Entry->me.ShortcutLetter = ShortcutLetter;
1685 Entry->me.Image = Image;
1686 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1687 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1688 Entry->UseGraphicsMode = UseGraphicsMode;
1689
1690 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1691 return Entry;
1692 } /* static LOADER_ENTRY * AddToolEntry() */
1693
1694 //
1695 // pre-boot driver functions
1696 //
1697
1698 static UINTN ScanDriverDir(IN CHAR16 *Path)
1699 {
1700 EFI_STATUS Status;
1701 REFIT_DIR_ITER DirIter;
1702 UINTN NumFound = 0;
1703 EFI_FILE_INFO *DirEntry;
1704 CHAR16 FileName[256];
1705
1706 CleanUpPathNameSlashes(Path);
1707 // look through contents of the directory
1708 DirIterOpen(SelfRootDir, Path, &DirIter);
1709 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1710 if (DirEntry->FileName[0] == '.')
1711 continue; // skip this
1712
1713 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1714 NumFound++;
1715 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1716 L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE);
1717 }
1718 Status = DirIterClose(&DirIter);
1719 if (Status != EFI_NOT_FOUND) {
1720 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1721 CheckError(Status, FileName);
1722 }
1723 return (NumFound);
1724 }
1725
1726 #ifdef __MAKEWITH_GNUEFI
1727 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1728 {
1729 EFI_STATUS Status;
1730 UINTN AllHandleCount;
1731 EFI_HANDLE *AllHandleBuffer;
1732 UINTN Index;
1733 UINTN HandleCount;
1734 EFI_HANDLE *HandleBuffer;
1735 UINT32 *HandleType;
1736 UINTN HandleIndex;
1737 BOOLEAN Parent;
1738 BOOLEAN Device;
1739
1740 Status = LibLocateHandle(AllHandles,
1741 NULL,
1742 NULL,
1743 &AllHandleCount,
1744 &AllHandleBuffer);
1745 if (EFI_ERROR(Status))
1746 return Status;
1747
1748 for (Index = 0; Index < AllHandleCount; Index++) {
1749 //
1750 // Scan the handle database
1751 //
1752 Status = LibScanHandleDatabase(NULL,
1753 NULL,
1754 AllHandleBuffer[Index],
1755 NULL,
1756 &HandleCount,
1757 &HandleBuffer,
1758 &HandleType);
1759 if (EFI_ERROR (Status))
1760 goto Done;
1761
1762 Device = TRUE;
1763 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1764 Device = FALSE;
1765 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1766 Device = FALSE;
1767
1768 if (Device) {
1769 Parent = FALSE;
1770 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1771 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1772 Parent = TRUE;
1773 } // for
1774
1775 if (!Parent) {
1776 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1777 Status = refit_call4_wrapper(BS->ConnectController,
1778 AllHandleBuffer[Index],
1779 NULL,
1780 NULL,
1781 TRUE);
1782 }
1783 }
1784 }
1785
1786 MyFreePool (HandleBuffer);
1787 MyFreePool (HandleType);
1788 }
1789
1790 Done:
1791 MyFreePool (AllHandleBuffer);
1792 return Status;
1793 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1794 #else
1795 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1796 BdsLibConnectAllDriversToAllControllers();
1797 return 0;
1798 }
1799 #endif
1800
1801 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1802 // directories specified by the user in the "scan_driver_dirs" configuration
1803 // file line.
1804 static VOID LoadDrivers(VOID)
1805 {
1806 CHAR16 *Directory, *SelfDirectory;
1807 UINTN i = 0, Length, NumFound = 0;
1808
1809 // load drivers from the subdirectories of rEFInd's home directory specified
1810 // in the DRIVER_DIRS constant.
1811 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1812 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
1813 CleanUpPathNameSlashes(SelfDirectory);
1814 MergeStrings(&SelfDirectory, Directory, L'\\');
1815 NumFound += ScanDriverDir(SelfDirectory);
1816 MyFreePool(Directory);
1817 MyFreePool(SelfDirectory);
1818 }
1819
1820 // Scan additional user-specified driver directories....
1821 i = 0;
1822 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1823 CleanUpPathNameSlashes(Directory);
1824 Length = StrLen(Directory);
1825 if (Length > 0) {
1826 NumFound += ScanDriverDir(Directory);
1827 } // if
1828 MyFreePool(Directory);
1829 } // while
1830
1831 // connect all devices
1832 if (NumFound > 0)
1833 ConnectAllDriversToAllControllers();
1834 } /* static VOID LoadDrivers() */
1835
1836 // Determine what (if any) type of legacy (BIOS) boot support is available
1837 static VOID FindLegacyBootType(VOID) {
1838 #ifdef __MAKEWITH_TIANO
1839 EFI_STATUS Status;
1840 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1841 #endif
1842
1843 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
1844
1845 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1846 // build environment, and then only with some EFI implementations....
1847 #ifdef __MAKEWITH_TIANO
1848 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1849 if (!EFI_ERROR (Status))
1850 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
1851 #endif
1852
1853 // Macs have their own system. If the firmware vendor code contains the
1854 // string "Apple", assume it's available. Note that this overrides the
1855 // UEFI type, and might yield false positives if the vendor string
1856 // contains "Apple" as part of something bigger, so this isn't 100%
1857 // perfect.
1858 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
1859 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
1860 } // static VOID FindLegacyBootType
1861
1862 // Warn the user if legacy OS scans are enabled but the firmware or this
1863 // application can't support them....
1864 static VOID WarnIfLegacyProblems() {
1865 BOOLEAN found = FALSE;
1866 UINTN i = 0;
1867
1868 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
1869 do {
1870 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
1871 found = TRUE;
1872 i++;
1873 } while ((i < NUM_SCAN_OPTIONS) && (!found));
1874 if (found) {
1875 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1876 Print(L"(BIOS) boot options; however, this is not possible because ");
1877 #ifdef __MAKEWITH_TIANO
1878 Print(L"your computer lacks\n");
1879 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
1880 #else
1881 Print(L"this program was\n");
1882 Print(L"compiled without the necessary support. Please visit\n");
1883 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1884 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1885 #endif
1886 PauseForKey();
1887 } // if (found)
1888 } // if no legacy support
1889 } // static VOID WarnIfLegacyProblems()
1890
1891 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1892 static VOID ScanForBootloaders(VOID) {
1893 UINTN i;
1894
1895 ScanVolumes();
1896
1897 // scan for loaders and tools, add them to the menu
1898 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1899 switch(GlobalConfig.ScanFor[i]) {
1900 case 'c': case 'C':
1901 ScanLegacyDisc();
1902 break;
1903 case 'h': case 'H':
1904 ScanLegacyInternal();
1905 break;
1906 case 'b': case 'B':
1907 ScanLegacyExternal();
1908 break;
1909 case 'm': case 'M':
1910 ScanUserConfigured(CONFIG_FILE_NAME);
1911 break;
1912 case 'e': case 'E':
1913 ScanExternal();
1914 break;
1915 case 'i': case 'I':
1916 ScanInternal();
1917 break;
1918 case 'o': case 'O':
1919 ScanOptical();
1920 break;
1921 } // switch()
1922 } // for
1923
1924 // assign shortcut keys
1925 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1926 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1927
1928 // wait for user ACK when there were errors
1929 FinishTextScreen(FALSE);
1930 } // static VOID ScanForBootloaders()
1931
1932 // Add the second-row tags containing built-in and external tools (EFI shell,
1933 // reboot, etc.)
1934 static VOID ScanForTools(VOID) {
1935 CHAR16 *FileName = NULL, Description[256];
1936 REFIT_MENU_ENTRY *TempMenuEntry;
1937 UINTN i, j, VolumeIndex;
1938
1939 for (i = 0; i < NUM_TOOLS; i++) {
1940 switch(GlobalConfig.ShowTools[i]) {
1941 case TAG_SHUTDOWN:
1942 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1943 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1944 AddMenuEntry(&MainMenu, TempMenuEntry);
1945 break;
1946 case TAG_REBOOT:
1947 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1948 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1949 AddMenuEntry(&MainMenu, TempMenuEntry);
1950 break;
1951 case TAG_ABOUT:
1952 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1953 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1954 AddMenuEntry(&MainMenu, TempMenuEntry);
1955 break;
1956 case TAG_EXIT:
1957 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1958 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1959 AddMenuEntry(&MainMenu, TempMenuEntry);
1960 break;
1961 case TAG_SHELL:
1962 j = 0;
1963 MyFreePool(FileName);
1964 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1965 if (FileExists(SelfRootDir, FileName)) {
1966 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
1967 'S', FALSE);
1968 }
1969 } // while
1970 break;
1971 case TAG_GPTSYNC:
1972 MyFreePool(FileName);
1973 FileName = StrDuplicate(L"\\efi\\tools\\gptsync.efi");
1974 if (FileExists(SelfRootDir, FileName)) {
1975 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1976 }
1977 break;
1978 case TAG_APPLE_RECOVERY:
1979 MyFreePool(FileName);
1980 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
1981 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1982 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
1983 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
1984 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
1985 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
1986 }
1987 } // for
1988 break;
1989 case TAG_MOK_TOOL:
1990 j = 0;
1991 MyFreePool(FileName);
1992 while ((FileName = FindCommaDelimited(MOK_NAMES, j++)) != NULL) {
1993 if (FileExists(SelfRootDir, FileName)) {
1994 SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
1995 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
1996 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
1997 }
1998 } // while
1999 if (FileExists(SelfDir, L"MokManager.efi")) {
2000 MyFreePool(FileName);
2001 FileName = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
2002 MergeStrings(&FileName, L"\\MokManager.efi", 0);
2003 SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
2004 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
2005 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
2006 }
2007 break;
2008 } // switch()
2009 MyFreePool(FileName);
2010 FileName = NULL;
2011 } // for
2012 } // static VOID ScanForTools
2013
2014 // Rescan for boot loaders
2015 VOID RescanAll(VOID) {
2016 EG_PIXEL BGColor;
2017
2018 BGColor.b = 255;
2019 BGColor.g = 175;
2020 BGColor.r = 100;
2021 BGColor.a = 0;
2022 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
2023 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
2024 MainMenu.Entries = NULL;
2025 MainMenu.EntryCount = 0;
2026 ReadConfig(CONFIG_FILE_NAME);
2027 ConnectAllDriversToAllControllers();
2028 ScanVolumes();
2029 ScanForBootloaders();
2030 ScanForTools();
2031 SetupScreen();
2032 } // VOID RescanAll()
2033
2034 #ifdef __MAKEWITH_TIANO
2035
2036 // Minimal initialization function
2037 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
2038 gST = SystemTable;
2039 // gImageHandle = ImageHandle;
2040 gBS = SystemTable->BootServices;
2041 // gRS = SystemTable->RuntimeServices;
2042 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
2043 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
2044
2045 InitializeConsoleSim();
2046 }
2047
2048 #endif
2049
2050 // Set up our own Secure Boot extensions....
2051 // Returns TRUE on success, FALSE otherwise
2052 static BOOLEAN SecureBootSetup(VOID) {
2053 EFI_STATUS Status;
2054 BOOLEAN Success = FALSE;
2055
2056 if (secure_mode() && ShimLoaded()) {
2057 Status = security_policy_install();
2058 if (Status == EFI_SUCCESS) {
2059 Success = TRUE;
2060 } else {
2061 Print(L"Failed to install MOK Secure Boot extensions");
2062 }
2063 }
2064 return Success;
2065 } // VOID SecureBootSetup()
2066
2067 // Remove our own Secure Boot extensions....
2068 // Returns TRUE on success, FALSE otherwise
2069 static BOOLEAN SecureBootUninstall(VOID) {
2070 EFI_STATUS Status;
2071 BOOLEAN Success = TRUE;
2072
2073 if (secure_mode()) {
2074 Status = security_policy_uninstall();
2075 if (Status != EFI_SUCCESS) {
2076 Success = FALSE;
2077 BeginTextScreen(L"Secure Boot Policy Failure");
2078 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
2079 PauseForKey();
2080 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2081 }
2082 }
2083 return Success;
2084 } // VOID SecureBootUninstall
2085
2086 //
2087 // main entry point
2088 //
2089 EFI_STATUS
2090 EFIAPI
2091 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2092 {
2093 EFI_STATUS Status;
2094 BOOLEAN MainLoopRunning = TRUE;
2095 BOOLEAN MokProtocol;
2096 REFIT_MENU_ENTRY *ChosenEntry;
2097 UINTN MenuExit, i;
2098 CHAR16 *Selection = NULL;
2099 EG_PIXEL BGColor;
2100
2101 // bootstrap
2102 InitializeLib(ImageHandle, SystemTable);
2103 Status = InitRefitLib(ImageHandle);
2104 if (EFI_ERROR(Status))
2105 return Status;
2106
2107 // read configuration
2108 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2109 FindLegacyBootType();
2110 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2111 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2112 ScanVolumes();
2113 ReadConfig(CONFIG_FILE_NAME);
2114
2115 InitScreen();
2116 WarnIfLegacyProblems();
2117 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2118
2119 // disable EFI watchdog timer
2120 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2121
2122 // further bootstrap (now with config available)
2123 MokProtocol = SecureBootSetup();
2124 LoadDrivers();
2125 ScanForBootloaders();
2126 ScanForTools();
2127 SetupScreen();
2128
2129 if (GlobalConfig.ScanDelay > 0) {
2130 BGColor.b = 255;
2131 BGColor.g = 175;
2132 BGColor.r = 100;
2133 BGColor.a = 0;
2134 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2135 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2136 refit_call1_wrapper(BS->Stall, 1000000);
2137 RescanAll();
2138 } // if
2139
2140 if (GlobalConfig.DefaultSelection)
2141 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2142
2143 while (MainLoopRunning) {
2144 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2145
2146 // The Escape key triggers a re-scan operation....
2147 if (MenuExit == MENU_EXIT_ESCAPE) {
2148 RescanAll();
2149 continue;
2150 }
2151
2152 switch (ChosenEntry->Tag) {
2153
2154 case TAG_REBOOT: // Reboot
2155 TerminateScreen();
2156 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2157 MainLoopRunning = FALSE; // just in case we get this far
2158 break;
2159
2160 case TAG_SHUTDOWN: // Shut Down
2161 TerminateScreen();
2162 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2163 MainLoopRunning = FALSE; // just in case we get this far
2164 break;
2165
2166 case TAG_ABOUT: // About rEFInd
2167 AboutrEFInd();
2168 break;
2169
2170 case TAG_LOADER: // Boot OS via .EFI loader
2171 StartLoader((LOADER_ENTRY *)ChosenEntry);
2172 break;
2173
2174 case TAG_LEGACY: // Boot legacy OS
2175 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2176 break;
2177
2178 #ifdef __MAKEWITH_TIANO
2179 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2180 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2181 break;
2182 #endif
2183
2184 case TAG_TOOL: // Start a EFI tool
2185 StartTool((LOADER_ENTRY *)ChosenEntry);
2186 break;
2187
2188 case TAG_EXIT: // Terminate rEFInd
2189 if ((MokProtocol) && !SecureBootUninstall()) {
2190 MainLoopRunning = FALSE; // just in case we get this far
2191 } else {
2192 BeginTextScreen(L" ");
2193 return EFI_SUCCESS;
2194 }
2195 break;
2196
2197 } // switch()
2198 MyFreePool(Selection);
2199 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2200 } // while()
2201
2202 // If we end up here, things have gone wrong. Try to reboot, and if that
2203 // fails, go into an endless loop.
2204 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2205 EndlessIdleLoop();
2206
2207 return EFI_SUCCESS;
2208 } /* efi_main() */