]> code.delx.au - refind/blob - refind/main.c
a8ecdf350f5f50b07f26a3b7be3c23a9edda9cbd
[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_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.6.2");
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(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->UseGraphicsMode = UseGraphicsMode;
1473
1474 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1475 return Entry;
1476 } /* static LOADER_ENTRY * AddToolEntry() */
1477
1478 //
1479 // pre-boot driver functions
1480 //
1481
1482 static UINTN ScanDriverDir(IN CHAR16 *Path)
1483 {
1484 EFI_STATUS Status;
1485 REFIT_DIR_ITER DirIter;
1486 UINTN NumFound = 0;
1487 EFI_FILE_INFO *DirEntry;
1488 CHAR16 FileName[256];
1489
1490 CleanUpPathNameSlashes(Path);
1491 // look through contents of the directory
1492 DirIterOpen(SelfRootDir, Path, &DirIter);
1493 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1494 if (DirEntry->FileName[0] == '.')
1495 continue; // skip this
1496
1497 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1498 NumFound++;
1499 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1500 L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE);
1501 }
1502 Status = DirIterClose(&DirIter);
1503 if (Status != EFI_NOT_FOUND) {
1504 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1505 CheckError(Status, FileName);
1506 }
1507 return (NumFound);
1508 }
1509
1510 #ifdef __MAKEWITH_GNUEFI
1511 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1512 {
1513 EFI_STATUS Status;
1514 UINTN AllHandleCount;
1515 EFI_HANDLE *AllHandleBuffer;
1516 UINTN Index;
1517 UINTN HandleCount;
1518 EFI_HANDLE *HandleBuffer;
1519 UINT32 *HandleType;
1520 UINTN HandleIndex;
1521 BOOLEAN Parent;
1522 BOOLEAN Device;
1523
1524 Status = LibLocateHandle(AllHandles,
1525 NULL,
1526 NULL,
1527 &AllHandleCount,
1528 &AllHandleBuffer);
1529 if (EFI_ERROR(Status))
1530 return Status;
1531
1532 for (Index = 0; Index < AllHandleCount; Index++) {
1533 //
1534 // Scan the handle database
1535 //
1536 Status = LibScanHandleDatabase(NULL,
1537 NULL,
1538 AllHandleBuffer[Index],
1539 NULL,
1540 &HandleCount,
1541 &HandleBuffer,
1542 &HandleType);
1543 if (EFI_ERROR (Status))
1544 goto Done;
1545
1546 Device = TRUE;
1547 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1548 Device = FALSE;
1549 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1550 Device = FALSE;
1551
1552 if (Device) {
1553 Parent = FALSE;
1554 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1555 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1556 Parent = TRUE;
1557 } // for
1558
1559 if (!Parent) {
1560 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1561 Status = refit_call4_wrapper(BS->ConnectController,
1562 AllHandleBuffer[Index],
1563 NULL,
1564 NULL,
1565 TRUE);
1566 }
1567 }
1568 }
1569
1570 MyFreePool (HandleBuffer);
1571 MyFreePool (HandleType);
1572 }
1573
1574 Done:
1575 MyFreePool (AllHandleBuffer);
1576 return Status;
1577 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1578 #else
1579 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1580 BdsLibConnectAllDriversToAllControllers();
1581 return 0;
1582 }
1583 #endif
1584
1585 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1586 // directories specified by the user in the "scan_driver_dirs" configuration
1587 // file line.
1588 static VOID LoadDrivers(VOID)
1589 {
1590 CHAR16 *Directory, *SelfDirectory;
1591 UINTN i = 0, Length, NumFound = 0;
1592
1593 // load drivers from the subdirectories of rEFInd's home directory specified
1594 // in the DRIVER_DIRS constant.
1595 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1596 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
1597 CleanUpPathNameSlashes(SelfDirectory);
1598 MergeStrings(&SelfDirectory, Directory, L'\\');
1599 NumFound += ScanDriverDir(SelfDirectory);
1600 MyFreePool(Directory);
1601 MyFreePool(SelfDirectory);
1602 }
1603
1604 // Scan additional user-specified driver directories....
1605 i = 0;
1606 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1607 CleanUpPathNameSlashes(Directory);
1608 Length = StrLen(Directory);
1609 if (Length > 0)
1610 NumFound += ScanDriverDir(Directory);
1611 MyFreePool(Directory);
1612 } // while
1613
1614 // connect all devices
1615 if (NumFound > 0)
1616 ConnectAllDriversToAllControllers();
1617 } /* static VOID LoadDrivers() */
1618
1619 // Determine what (if any) type of legacy (BIOS) boot support is available
1620 static VOID FindLegacyBootType(VOID) {
1621 #ifdef __MAKEWITH_TIANO
1622 EFI_STATUS Status;
1623 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1624 #endif
1625
1626 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
1627
1628 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1629 // build environment, and then only with some implementations....
1630 #ifdef __MAKEWITH_TIANO
1631 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1632 if (!EFI_ERROR (Status))
1633 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
1634 #endif
1635
1636 // Macs have their own system. If the firmware vendor code contains the
1637 // string "Apple", assume it's available. Note that this overrides the
1638 // UEFI type, and might yield false positives if the vendor string
1639 // contains "Apple" as part of something bigger, so this isn't 100%
1640 // perfect.
1641 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
1642 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
1643 } // static VOID FindLegacyBootType
1644
1645 // Warn the user if legacy OS scans are enabled but the firmware or this
1646 // application can't support them....
1647 static VOID WarnIfLegacyProblems() {
1648 BOOLEAN found = FALSE;
1649 UINTN i = 0;
1650
1651 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
1652 do {
1653 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
1654 found = TRUE;
1655 i++;
1656 } while ((i < NUM_SCAN_OPTIONS) && (!found));
1657 if (found) {
1658 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1659 Print(L"(BIOS) boot options; however, this is not possible because ");
1660 #ifdef __MAKEWITH_TIANO
1661 Print(L"your computer lacks\n");
1662 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
1663 #else
1664 Print(L"this program was\n");
1665 Print(L"compiled without the necessary support. Please visit\n");
1666 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1667 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1668 #endif
1669 PauseForKey();
1670 } // if (found)
1671 } // if no legacy support
1672 } // static VOID WarnIfLegacyProblems()
1673
1674 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1675 static VOID ScanForBootloaders(VOID) {
1676 UINTN i;
1677
1678 ScanVolumes();
1679
1680 // scan for loaders and tools, add them to the menu
1681 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1682 switch(GlobalConfig.ScanFor[i]) {
1683 case 'c': case 'C':
1684 ScanLegacyDisc();
1685 break;
1686 case 'h': case 'H':
1687 ScanLegacyInternal();
1688 break;
1689 case 'b': case 'B':
1690 ScanLegacyExternal();
1691 break;
1692 case 'm': case 'M':
1693 ScanUserConfigured();
1694 break;
1695 case 'e': case 'E':
1696 ScanExternal();
1697 break;
1698 case 'i': case 'I':
1699 ScanInternal();
1700 break;
1701 case 'o': case 'O':
1702 ScanOptical();
1703 break;
1704 } // switch()
1705 } // for
1706
1707 // assign shortcut keys
1708 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1709 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1710
1711 // wait for user ACK when there were errors
1712 FinishTextScreen(FALSE);
1713 } // static VOID ScanForBootloaders()
1714
1715 // Add the second-row tags containing built-in and external tools (EFI shell,
1716 // reboot, etc.)
1717 static VOID ScanForTools(VOID) {
1718 CHAR16 *FileName = NULL;
1719 REFIT_MENU_ENTRY *TempMenuEntry;
1720 UINTN i, j;
1721
1722 for (i = 0; i < NUM_TOOLS; i++) {
1723 switch(GlobalConfig.ShowTools[i]) {
1724 case TAG_SHUTDOWN:
1725 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1726 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1727 AddMenuEntry(&MainMenu, TempMenuEntry);
1728 break;
1729 case TAG_REBOOT:
1730 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1731 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1732 AddMenuEntry(&MainMenu, TempMenuEntry);
1733 break;
1734 case TAG_ABOUT:
1735 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1736 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1737 AddMenuEntry(&MainMenu, TempMenuEntry);
1738 break;
1739 case TAG_EXIT:
1740 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1741 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1742 AddMenuEntry(&MainMenu, TempMenuEntry);
1743 break;
1744 case TAG_SHELL:
1745 j = 0;
1746 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1747 if (FileExists(SelfRootDir, FileName)) {
1748 AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE);
1749 }
1750 } // while
1751 break;
1752 case TAG_GPTSYNC:
1753 MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1754 if (FileExists(SelfRootDir, FileName)) {
1755 AddToolEntry(FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1756 }
1757 break;
1758 } // switch()
1759 MyFreePool(FileName);
1760 FileName = NULL;
1761 } // for
1762 } // static VOID ScanForTools
1763
1764 // Rescan for boot loaders
1765 VOID RescanAll(VOID) {
1766 EG_PIXEL BGColor;
1767
1768 BGColor.b = 255;
1769 BGColor.g = 175;
1770 BGColor.r = 100;
1771 BGColor.a = 0;
1772 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
1773 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
1774 MainMenu.Entries = NULL;
1775 MainMenu.EntryCount = 0;
1776 ReadConfig();
1777 ConnectAllDriversToAllControllers();
1778 ScanForBootloaders();
1779 ScanForTools();
1780 SetupScreen();
1781 } // VOID RescanAll()
1782
1783 #ifndef __MAKEWITH_GNUEFI
1784
1785 // Minimal initialization function
1786 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
1787 gST = SystemTable;
1788 // gImageHandle = ImageHandle;
1789 gBS = SystemTable->BootServices;
1790 // gRS = SystemTable->RuntimeServices;
1791 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
1792 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
1793
1794 InitializeConsoleSim();
1795 }
1796
1797 #endif
1798
1799 //
1800 // main entry point
1801 //
1802 EFI_STATUS
1803 EFIAPI
1804 efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
1805 {
1806 EFI_STATUS Status;
1807 BOOLEAN MainLoopRunning = TRUE;
1808 REFIT_MENU_ENTRY *ChosenEntry;
1809 UINTN MenuExit, i;
1810 CHAR16 *Selection;
1811 EG_PIXEL BGColor;
1812
1813 // bootstrap
1814 InitializeLib(ImageHandle, SystemTable);
1815 InitScreen();
1816 Status = InitRefitLib(ImageHandle);
1817 if (EFI_ERROR(Status))
1818 return Status;
1819
1820 // read configuration
1821 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
1822 FindLegacyBootType();
1823 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
1824 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
1825 ReadConfig();
1826 WarnIfLegacyProblems();
1827 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
1828
1829 // disable EFI watchdog timer
1830 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
1831
1832 // further bootstrap (now with config available)
1833 SetupScreen();
1834 if (GlobalConfig.ScanDelay > 0) {
1835 BGColor.b = 255;
1836 BGColor.g = 175;
1837 BGColor.r = 100;
1838 BGColor.a = 0;
1839 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
1840 for (i = 0; i < GlobalConfig.ScanDelay; i++)
1841 refit_call1_wrapper(BS->Stall, 1000000);
1842 } // if
1843 LoadDrivers();
1844 ScanForBootloaders();
1845 ScanForTools();
1846
1847 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
1848 while (MainLoopRunning) {
1849 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
1850
1851 // The Escape key triggers a re-scan operation....
1852 if (MenuExit == MENU_EXIT_ESCAPE) {
1853 RescanAll();
1854 continue;
1855 }
1856
1857 switch (ChosenEntry->Tag) {
1858
1859 case TAG_REBOOT: // Reboot
1860 TerminateScreen();
1861 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1862 MainLoopRunning = FALSE; // just in case we get this far
1863 break;
1864
1865 case TAG_SHUTDOWN: // Shut Down
1866 TerminateScreen();
1867 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1868 MainLoopRunning = FALSE; // just in case we get this far
1869 break;
1870
1871 case TAG_ABOUT: // About rEFInd
1872 AboutrEFInd();
1873 break;
1874
1875 case TAG_LOADER: // Boot OS via .EFI loader
1876 StartLoader((LOADER_ENTRY *)ChosenEntry);
1877 break;
1878
1879 case TAG_LEGACY: // Boot legacy OS
1880 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
1881 break;
1882
1883 #ifdef __MAKEWITH_TIANO
1884 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
1885 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
1886 break;
1887 #endif // __MAKEWITH_TIANO
1888
1889 case TAG_TOOL: // Start a EFI tool
1890 StartTool((LOADER_ENTRY *)ChosenEntry);
1891 break;
1892
1893 case TAG_EXIT: // Terminate rEFInd
1894 BeginTextScreen(L" ");
1895 return EFI_SUCCESS;
1896 break;
1897
1898 } // switch()
1899 MyFreePool(Selection);
1900 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
1901 } // while()
1902
1903 // If we end up here, things have gone wrong. Try to reboot, and if that
1904 // fails, go into an endless loop.
1905 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1906 EndlessIdleLoop();
1907
1908 return EFI_SUCCESS;
1909 } /* efi_main() */