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