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