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