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