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