]> code.delx.au - refind/blob - refind/main.c
Version supports Secure Boot/MOK verification of binaries.
[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 "mok2.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"
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,
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.DontScan))) {
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(DirEntry->FileName, L"TextMode.efi") == 0 ||
875 StriCmp(DirEntry->FileName, L"ebounce.efi") == 0 ||
876 StriCmp(DirEntry->FileName, L"GraphicsConsole.efi") == 0 ||
877 StriCmp(Extension, L".icns") == 0 ||
878 StriSubCmp(L"shell", DirEntry->FileName))
879 continue; // skip this
880
881 if (Path)
882 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
883 else
884 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
885 CleanUpPathNameSlashes(FileName);
886 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
887 if (NewLoader != NULL) {
888 NewLoader->FileName = StrDuplicate(FileName);
889 NewLoader->TimeStamp = DirEntry->ModificationTime;
890 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
891 } // if
892 MyFreePool(Extension);
893 } // while
894 NewLoader = LoaderList;
895 while (NewLoader != NULL) {
896 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
897 NewLoader = NewLoader->NextEntry;
898 } // while
899 CleanUpLoaderList(LoaderList);
900 Status = DirIterClose(&DirIter);
901 if (Status != EFI_NOT_FOUND) {
902 if (Path)
903 SPrint(FileName, 255, L"while scanning the %s directory", Path);
904 else
905 StrCpy(FileName, L"while scanning the root directory");
906 CheckError(Status, FileName);
907 } // if (Status != EFI_NOT_FOUND)
908 } // if not scanning our own directory
909 } /* static VOID ScanLoaderDir() */
910
911 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
912 EFI_STATUS Status;
913 REFIT_DIR_ITER EfiDirIter;
914 EFI_FILE_INFO *EfiDirEntry;
915 CHAR16 FileName[256], *Directory, *MatchPatterns;
916 UINTN i, Length;
917
918 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
919 if (GlobalConfig.ScanAllLinux)
920 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
921
922 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
923 // check for Mac OS X boot loader
924 if (!IsIn(L"\\System\\Library\\CoreServices", GlobalConfig.DontScan)) {
925 StrCpy(FileName, MACOSX_LOADER_PATH);
926 if (FileExists(Volume->RootDir, FileName)) {
927 AddLoaderEntry(FileName, L"Mac OS X", Volume);
928 }
929
930 // check for XOM
931 StrCpy(FileName, L"\\System\\Library\\CoreServices\\xom.efi");
932 if (FileExists(Volume->RootDir, FileName)) {
933 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
934 }
935 } // if Mac directory not in GlobalConfig.DontScan list
936
937 // check for Microsoft boot loader/menu
938 StrCpy(FileName, L"\\EFI\\Microsoft\\Boot\\Bootmgfw.efi");
939 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"\\EFI\\Microsoft\\Boot", GlobalConfig.DontScan)) {
940 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
941 }
942
943 // scan the root directory for EFI executables
944 ScanLoaderDir(Volume, L"\\", MatchPatterns);
945
946 // scan subdirectories of the EFI directory (as per the standard)
947 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
948 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
949 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
950 continue; // skip this, doesn't contain boot loaders
951 SPrint(FileName, 255, L"\\EFI\\%s", EfiDirEntry->FileName);
952 ScanLoaderDir(Volume, FileName, MatchPatterns);
953 } // while()
954 Status = DirIterClose(&EfiDirIter);
955 if (Status != EFI_NOT_FOUND)
956 CheckError(Status, L"while scanning the EFI directory");
957
958 // Scan user-specified (or additional default) directories....
959 i = 0;
960 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
961 CleanUpPathNameSlashes(Directory);
962 Length = StrLen(Directory);
963 if (Length > 0)
964 ScanLoaderDir(Volume, Directory, MatchPatterns);
965 MyFreePool(Directory);
966 } // while
967 } // if
968 } // static VOID ScanEfiFiles()
969
970 // Scan internal disks for valid EFI boot loaders....
971 static VOID ScanInternal(VOID) {
972 UINTN VolumeIndex;
973
974 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
975 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
976 ScanEfiFiles(Volumes[VolumeIndex]);
977 }
978 } // for
979 } // static VOID ScanInternal()
980
981 // Scan external disks for valid EFI boot loaders....
982 static VOID ScanExternal(VOID) {
983 UINTN VolumeIndex;
984
985 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
986 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
987 ScanEfiFiles(Volumes[VolumeIndex]);
988 }
989 } // for
990 } // static VOID ScanExternal()
991
992 // Scan internal disks for valid EFI boot loaders....
993 static VOID ScanOptical(VOID) {
994 UINTN VolumeIndex;
995
996 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
997 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
998 ScanEfiFiles(Volumes[VolumeIndex]);
999 }
1000 } // for
1001 } // static VOID ScanOptical()
1002
1003 //
1004 // legacy boot functions
1005 //
1006
1007 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
1008 {
1009 EFI_STATUS Status;
1010 UINT8 SectorBuffer[512];
1011 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
1012 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1013 UINTN LogicalPartitionIndex = 4;
1014 UINTN i;
1015 BOOLEAN HaveBootCode;
1016
1017 // read MBR
1018 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1019 if (EFI_ERROR(Status))
1020 return Status;
1021 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1022 return EFI_NOT_FOUND; // safety measure #1
1023
1024 // add boot code if necessary
1025 HaveBootCode = FALSE;
1026 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
1027 if (SectorBuffer[i] != 0) {
1028 HaveBootCode = TRUE;
1029 break;
1030 }
1031 }
1032 if (!HaveBootCode) {
1033 // no boot code found in the MBR, add the syslinux MBR code
1034 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
1035 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
1036 }
1037
1038 // set the partition active
1039 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1040 ExtBase = 0;
1041 for (i = 0; i < 4; i++) {
1042 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1043 return EFI_NOT_FOUND; // safety measure #2
1044 if (i == PartitionIndex)
1045 MbrTable[i].Flags = 0x80;
1046 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1047 MbrTable[i].Flags = 0x80;
1048 ExtBase = MbrTable[i].StartLBA;
1049 } else
1050 MbrTable[i].Flags = 0x00;
1051 }
1052
1053 // write MBR
1054 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1055 if (EFI_ERROR(Status))
1056 return Status;
1057
1058 if (PartitionIndex >= 4) {
1059 // we have to activate a logical partition, so walk the EMBR chain
1060
1061 // NOTE: ExtBase was set above while looking at the MBR table
1062 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1063 // read current EMBR
1064 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1065 if (EFI_ERROR(Status))
1066 return Status;
1067 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1068 return EFI_NOT_FOUND; // safety measure #3
1069
1070 // scan EMBR, set appropriate partition active
1071 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1072 NextExtCurrent = 0;
1073 for (i = 0; i < 4; i++) {
1074 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1075 return EFI_NOT_FOUND; // safety measure #4
1076 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1077 break;
1078 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1079 // link to next EMBR
1080 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1081 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1082 break;
1083 } else {
1084 // logical partition
1085 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1086 LogicalPartitionIndex++;
1087 }
1088 }
1089
1090 // write current EMBR
1091 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1092 if (EFI_ERROR(Status))
1093 return Status;
1094
1095 if (PartitionIndex < LogicalPartitionIndex)
1096 break; // stop the loop, no need to touch further EMBRs
1097 }
1098
1099 }
1100
1101 return EFI_SUCCESS;
1102 } /* static EFI_STATUS ActivateMbrPartition() */
1103
1104 // early 2006 Core Duo / Core Solo models
1105 static UINT8 LegacyLoaderDevicePath1Data[] = {
1106 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1107 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1108 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1109 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1110 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1111 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1112 };
1113 // mid-2006 Mac Pro (and probably other Core 2 models)
1114 static UINT8 LegacyLoaderDevicePath2Data[] = {
1115 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1116 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1117 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1118 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1119 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1120 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1121 };
1122 // mid-2007 MBP ("Santa Rosa" based models)
1123 static UINT8 LegacyLoaderDevicePath3Data[] = {
1124 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1125 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1126 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1127 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1128 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1129 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1130 };
1131 // early-2008 MBA
1132 static UINT8 LegacyLoaderDevicePath4Data[] = {
1133 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1134 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1135 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1136 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1137 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1138 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1139 };
1140 // late-2008 MB/MBP (NVidia chipset)
1141 static UINT8 LegacyLoaderDevicePath5Data[] = {
1142 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1143 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1144 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1145 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1146 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1147 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1148 };
1149
1150 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1151 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1152 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1153 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1154 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1155 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1156 NULL
1157 };
1158
1159 #define MAX_DISCOVERED_PATHS (16)
1160
1161 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1162 {
1163 EFI_STATUS Status;
1164 EG_IMAGE *BootLogoImage;
1165 UINTN ErrorInStep = 0;
1166 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1167
1168 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
1169
1170 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1171 if (BootLogoImage != NULL)
1172 BltImageAlpha(BootLogoImage,
1173 (UGAWidth - BootLogoImage->Width ) >> 1,
1174 (UGAHeight - BootLogoImage->Height) >> 1,
1175 &StdBackgroundPixel);
1176
1177 if (Entry->Volume->IsMbrPartition) {
1178 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1179 }
1180
1181 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1182
1183 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", 0, &ErrorInStep, TRUE);
1184 if (Status == EFI_NOT_FOUND) {
1185 if (ErrorInStep == 1) {
1186 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1187 } else if (ErrorInStep == 3) {
1188 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1189 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1190 }
1191 }
1192 FinishExternalScreen();
1193 } /* static VOID StartLegacy() */
1194
1195 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1196 #ifdef __MAKEWITH_TIANO
1197 static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry)
1198 {
1199 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1200
1201 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1202 BdsLibDoLegacyBoot(Entry->BdsOption);
1203
1204 // If we get here, it means that there was a failure....
1205 Print(L"Failure booting legacy (BIOS) OS.");
1206 PauseForKey();
1207 FinishExternalScreen();
1208 } // static VOID StartLegacyUEFI()
1209 #endif // __MAKEWITH_TIANO
1210
1211 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1212 {
1213 LEGACY_ENTRY *Entry, *SubEntry;
1214 REFIT_MENU_SCREEN *SubScreen;
1215 CHAR16 *VolDesc;
1216 CHAR16 ShortcutLetter = 0;
1217
1218 if (LoaderTitle == NULL) {
1219 if (Volume->OSName != NULL) {
1220 LoaderTitle = Volume->OSName;
1221 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1222 ShortcutLetter = LoaderTitle[0];
1223 } else
1224 LoaderTitle = L"Legacy OS";
1225 }
1226 if (Volume->VolName != NULL)
1227 VolDesc = Volume->VolName;
1228 else
1229 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1230
1231 // prepare the menu entry
1232 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1233 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1234 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1235 Entry->me.Tag = TAG_LEGACY;
1236 Entry->me.Row = 0;
1237 Entry->me.ShortcutLetter = ShortcutLetter;
1238 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1239 Entry->me.BadgeImage = Volume->VolBadgeImage;
1240 Entry->Volume = Volume;
1241 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1242 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1243 Entry->Enabled = TRUE;
1244
1245 // create the submenu
1246 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1247 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1248 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1249 SubScreen->TitleImage = Entry->me.Image;
1250
1251 // default entry
1252 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1253 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1254 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1255 SubEntry->me.Tag = TAG_LEGACY;
1256 SubEntry->Volume = Entry->Volume;
1257 SubEntry->LoadOptions = Entry->LoadOptions;
1258 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1259
1260 AddMenuEntry(SubScreen, &MenuEntryReturn);
1261 Entry->me.SubScreen = SubScreen;
1262 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1263 return Entry;
1264 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1265
1266
1267 #ifdef __MAKEWITH_TIANO
1268 // default volume badge icon based on disk kind
1269 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1270 EG_IMAGE * Badge = NULL;
1271
1272 switch (DiskType) {
1273 case BBS_HARDDISK:
1274 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1275 break;
1276 case BBS_USB:
1277 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1278 break;
1279 case BBS_CDROM:
1280 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1281 break;
1282 } // switch()
1283 return Badge;
1284 } // static EG_IMAGE * GetDiskBadge()
1285
1286 /**
1287 Create a rEFInd boot option from a Legacy BIOS protocol option.
1288 */
1289 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1290 {
1291 LEGACY_ENTRY *Entry, *SubEntry;
1292 REFIT_MENU_SCREEN *SubScreen;
1293 CHAR16 ShortcutLetter = 0;
1294 CHAR16 *LegacyDescription = BdsOption->Description;
1295
1296 // ScanVolume(Volume);
1297
1298 // prepare the menu entry
1299 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1300 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1301 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1302 Entry->me.Tag = TAG_LEGACY_UEFI;
1303 Entry->me.Row = 0;
1304 Entry->me.ShortcutLetter = ShortcutLetter;
1305 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1306 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1307 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1308 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1309 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1310 Entry->BdsOption = BdsOption;
1311 Entry->Enabled = TRUE;
1312
1313 // create the submenu
1314 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1315 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1316 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1317 SubScreen->TitleImage = Entry->me.Image;
1318
1319 // default entry
1320 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1321 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1322 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1323 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1324 Entry->BdsOption = BdsOption;
1325 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1326
1327 AddMenuEntry(SubScreen, &MenuEntryReturn);
1328 Entry->me.SubScreen = SubScreen;
1329 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1330 return Entry;
1331 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1332
1333 /**
1334 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1335 In testing, protocol has not been implemented on Macs but has been
1336 implemented on several Dell PCs and an ASUS motherboard.
1337 Restricts output to disks of the specified DiskType.
1338 */
1339 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1340 {
1341 EFI_STATUS Status;
1342 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1343 UINT16 *BootOrder = NULL;
1344 UINTN Index = 0;
1345 CHAR16 BootOption[10];
1346 UINTN BootOrderSize = 0;
1347 CHAR16 Buffer[20];
1348 BDS_COMMON_OPTION *BdsOption;
1349 LIST_ENTRY TempList;
1350 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1351 // REFIT_VOLUME Volume;
1352
1353 InitializeListHead (&TempList);
1354 ZeroMem (Buffer, sizeof (Buffer));
1355
1356 // If LegacyBios protocol is not implemented on this platform, then
1357 //we do not support this type of legacy boot on this machine.
1358 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1359 if (EFI_ERROR (Status))
1360 return;
1361
1362 // Grab the boot order
1363 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1364 if (BootOrder == NULL) {
1365 BootOrderSize = 0;
1366 }
1367
1368 Index = 0;
1369 while (Index < BootOrderSize / sizeof (UINT16))
1370 {
1371 // Grab each boot option variable from the boot order, and convert
1372 // the variable into a BDS boot option
1373 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1374 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1375
1376 if (BdsOption != NULL) {
1377 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1378
1379 // Only add the entry if it is of a requested type (e.g. USB, HD)
1380
1381 // Two checks necessary because some systems return EFI boot loaders
1382 // with a DeviceType value that would inappropriately include them
1383 // as legacy loaders....
1384 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1385 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1386 }
1387 }
1388 Index++;
1389 }
1390 } /* static VOID ScanLegacyUEFI() */
1391 #else
1392 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1393 #endif // __MAKEWITH_TIANO
1394
1395 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1396 UINTN VolumeIndex2;
1397 BOOLEAN ShowVolume, HideIfOthersFound;
1398
1399 ShowVolume = FALSE;
1400 HideIfOthersFound = FALSE;
1401 if (Volume->IsAppleLegacy) {
1402 ShowVolume = TRUE;
1403 HideIfOthersFound = TRUE;
1404 } else if (Volume->HasBootCode) {
1405 ShowVolume = TRUE;
1406 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1407 Volume->BlockIOOffset == 0 &&
1408 Volume->OSName == NULL)
1409 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1410 HideIfOthersFound = TRUE;
1411 }
1412 if (HideIfOthersFound) {
1413 // check for other bootable entries on the same disk
1414 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1415 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1416 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1417 ShowVolume = FALSE;
1418 }
1419 }
1420
1421 if (ShowVolume)
1422 AddLegacyEntry(NULL, Volume);
1423 } // static VOID ScanLegacyVolume()
1424
1425 // Scan attached optical discs for legacy (BIOS) boot code
1426 // and add anything found to the list....
1427 static VOID ScanLegacyDisc(VOID)
1428 {
1429 UINTN VolumeIndex;
1430 REFIT_VOLUME *Volume;
1431
1432 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1433 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1434 Volume = Volumes[VolumeIndex];
1435 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1436 ScanLegacyVolume(Volume, VolumeIndex);
1437 } // for
1438 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1439 ScanLegacyUEFI(BBS_CDROM);
1440 }
1441 } /* static VOID ScanLegacyDisc() */
1442
1443 // Scan internal hard disks for legacy (BIOS) boot code
1444 // and add anything found to the list....
1445 static VOID ScanLegacyInternal(VOID)
1446 {
1447 UINTN VolumeIndex;
1448 REFIT_VOLUME *Volume;
1449
1450 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1451 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1452 Volume = Volumes[VolumeIndex];
1453 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1454 ScanLegacyVolume(Volume, VolumeIndex);
1455 } // for
1456 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1457 ScanLegacyUEFI(BBS_HARDDISK);
1458 }
1459 } /* static VOID ScanLegacyInternal() */
1460
1461 // Scan external disks for legacy (BIOS) boot code
1462 // and add anything found to the list....
1463 static VOID ScanLegacyExternal(VOID)
1464 {
1465 UINTN VolumeIndex;
1466 REFIT_VOLUME *Volume;
1467
1468 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1469 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1470 Volume = Volumes[VolumeIndex];
1471 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1472 ScanLegacyVolume(Volume, VolumeIndex);
1473 } // for
1474 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1475 ScanLegacyUEFI(BBS_USB);
1476 }
1477 } /* static VOID ScanLegacyExternal() */
1478
1479 //
1480 // pre-boot tool functions
1481 //
1482
1483 static VOID StartTool(IN LOADER_ENTRY *Entry)
1484 {
1485 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1486 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1487 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1488 FinishExternalScreen();
1489 } /* static VOID StartTool() */
1490
1491 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1492 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1493 {
1494 LOADER_ENTRY *Entry;
1495 CHAR16 *TitleStr = NULL;
1496
1497 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1498
1499 MergeStrings(&TitleStr, L"Start ", 0);
1500 MergeStrings(&TitleStr, LoaderTitle, 0);
1501 Entry->me.Title = TitleStr;
1502 Entry->me.Tag = TAG_TOOL;
1503 Entry->me.Row = 1;
1504 Entry->me.ShortcutLetter = ShortcutLetter;
1505 Entry->me.Image = Image;
1506 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1507 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1508 Entry->UseGraphicsMode = UseGraphicsMode;
1509
1510 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1511 return Entry;
1512 } /* static LOADER_ENTRY * AddToolEntry() */
1513
1514 //
1515 // pre-boot driver functions
1516 //
1517
1518 static UINTN ScanDriverDir(IN CHAR16 *Path)
1519 {
1520 EFI_STATUS Status;
1521 REFIT_DIR_ITER DirIter;
1522 UINTN NumFound = 0;
1523 EFI_FILE_INFO *DirEntry;
1524 CHAR16 FileName[256];
1525
1526 CleanUpPathNameSlashes(Path);
1527 // look through contents of the directory
1528 DirIterOpen(SelfRootDir, Path, &DirIter);
1529 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1530 if (DirEntry->FileName[0] == '.')
1531 continue; // skip this
1532
1533 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1534 NumFound++;
1535 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1536 L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE);
1537 }
1538 Status = DirIterClose(&DirIter);
1539 if (Status != EFI_NOT_FOUND) {
1540 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1541 CheckError(Status, FileName);
1542 }
1543 return (NumFound);
1544 }
1545
1546 #ifdef __MAKEWITH_GNUEFI
1547 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1548 {
1549 EFI_STATUS Status;
1550 UINTN AllHandleCount;
1551 EFI_HANDLE *AllHandleBuffer;
1552 UINTN Index;
1553 UINTN HandleCount;
1554 EFI_HANDLE *HandleBuffer;
1555 UINT32 *HandleType;
1556 UINTN HandleIndex;
1557 BOOLEAN Parent;
1558 BOOLEAN Device;
1559
1560 Status = LibLocateHandle(AllHandles,
1561 NULL,
1562 NULL,
1563 &AllHandleCount,
1564 &AllHandleBuffer);
1565 if (EFI_ERROR(Status))
1566 return Status;
1567
1568 for (Index = 0; Index < AllHandleCount; Index++) {
1569 //
1570 // Scan the handle database
1571 //
1572 Status = LibScanHandleDatabase(NULL,
1573 NULL,
1574 AllHandleBuffer[Index],
1575 NULL,
1576 &HandleCount,
1577 &HandleBuffer,
1578 &HandleType);
1579 if (EFI_ERROR (Status))
1580 goto Done;
1581
1582 Device = TRUE;
1583 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1584 Device = FALSE;
1585 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1586 Device = FALSE;
1587
1588 if (Device) {
1589 Parent = FALSE;
1590 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1591 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1592 Parent = TRUE;
1593 } // for
1594
1595 if (!Parent) {
1596 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1597 Status = refit_call4_wrapper(BS->ConnectController,
1598 AllHandleBuffer[Index],
1599 NULL,
1600 NULL,
1601 TRUE);
1602 }
1603 }
1604 }
1605
1606 MyFreePool (HandleBuffer);
1607 MyFreePool (HandleType);
1608 }
1609
1610 Done:
1611 MyFreePool (AllHandleBuffer);
1612 return Status;
1613 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1614 #else
1615 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1616 BdsLibConnectAllDriversToAllControllers();
1617 return 0;
1618 }
1619 #endif
1620
1621 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1622 // directories specified by the user in the "scan_driver_dirs" configuration
1623 // file line.
1624 static VOID LoadDrivers(VOID)
1625 {
1626 CHAR16 *Directory, *SelfDirectory;
1627 UINTN i = 0, Length, NumFound = 0;
1628
1629 // load drivers from the subdirectories of rEFInd's home directory specified
1630 // in the DRIVER_DIRS constant.
1631 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1632 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
1633 CleanUpPathNameSlashes(SelfDirectory);
1634 MergeStrings(&SelfDirectory, Directory, L'\\');
1635 NumFound += ScanDriverDir(SelfDirectory);
1636 MyFreePool(Directory);
1637 MyFreePool(SelfDirectory);
1638 }
1639
1640 // Scan additional user-specified driver directories....
1641 i = 0;
1642 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1643 CleanUpPathNameSlashes(Directory);
1644 Length = StrLen(Directory);
1645 if (Length > 0) {
1646 NumFound += ScanDriverDir(Directory);
1647 } // if
1648 MyFreePool(Directory);
1649 } // while
1650
1651 // connect all devices
1652 if (NumFound > 0)
1653 ConnectAllDriversToAllControllers();
1654 } /* static VOID LoadDrivers() */
1655
1656 // Determine what (if any) type of legacy (BIOS) boot support is available
1657 static VOID FindLegacyBootType(VOID) {
1658 #ifdef __MAKEWITH_TIANO
1659 EFI_STATUS Status;
1660 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1661 #endif
1662
1663 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
1664
1665 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1666 // build environment, and then only with some implementations....
1667 #ifdef __MAKEWITH_TIANO
1668 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1669 if (!EFI_ERROR (Status))
1670 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
1671 #endif
1672
1673 // Macs have their own system. If the firmware vendor code contains the
1674 // string "Apple", assume it's available. Note that this overrides the
1675 // UEFI type, and might yield false positives if the vendor string
1676 // contains "Apple" as part of something bigger, so this isn't 100%
1677 // perfect.
1678 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
1679 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
1680 } // static VOID FindLegacyBootType
1681
1682 // Warn the user if legacy OS scans are enabled but the firmware or this
1683 // application can't support them....
1684 static VOID WarnIfLegacyProblems() {
1685 BOOLEAN found = FALSE;
1686 UINTN i = 0;
1687
1688 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
1689 do {
1690 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
1691 found = TRUE;
1692 i++;
1693 } while ((i < NUM_SCAN_OPTIONS) && (!found));
1694 if (found) {
1695 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1696 Print(L"(BIOS) boot options; however, this is not possible because ");
1697 #ifdef __MAKEWITH_TIANO
1698 Print(L"your computer lacks\n");
1699 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
1700 #else
1701 Print(L"this program was\n");
1702 Print(L"compiled without the necessary support. Please visit\n");
1703 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1704 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1705 #endif
1706 PauseForKey();
1707 } // if (found)
1708 } // if no legacy support
1709 } // static VOID WarnIfLegacyProblems()
1710
1711 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1712 static VOID ScanForBootloaders(VOID) {
1713 UINTN i;
1714
1715 ScanVolumes();
1716
1717 // scan for loaders and tools, add them to the menu
1718 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1719 switch(GlobalConfig.ScanFor[i]) {
1720 case 'c': case 'C':
1721 ScanLegacyDisc();
1722 break;
1723 case 'h': case 'H':
1724 ScanLegacyInternal();
1725 break;
1726 case 'b': case 'B':
1727 ScanLegacyExternal();
1728 break;
1729 case 'm': case 'M':
1730 ScanUserConfigured();
1731 break;
1732 case 'e': case 'E':
1733 ScanExternal();
1734 break;
1735 case 'i': case 'I':
1736 ScanInternal();
1737 break;
1738 case 'o': case 'O':
1739 ScanOptical();
1740 break;
1741 } // switch()
1742 } // for
1743
1744 // assign shortcut keys
1745 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1746 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1747
1748 // wait for user ACK when there were errors
1749 FinishTextScreen(FALSE);
1750 } // static VOID ScanForBootloaders()
1751
1752 // Add the second-row tags containing built-in and external tools (EFI shell,
1753 // reboot, etc.)
1754 static VOID ScanForTools(VOID) {
1755 CHAR16 *FileName = NULL, Description[256];
1756 REFIT_MENU_ENTRY *TempMenuEntry;
1757 UINTN i, j, VolumeIndex;
1758
1759 for (i = 0; i < NUM_TOOLS; i++) {
1760 switch(GlobalConfig.ShowTools[i]) {
1761 case TAG_SHUTDOWN:
1762 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1763 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1764 AddMenuEntry(&MainMenu, TempMenuEntry);
1765 break;
1766 case TAG_REBOOT:
1767 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1768 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1769 AddMenuEntry(&MainMenu, TempMenuEntry);
1770 break;
1771 case TAG_ABOUT:
1772 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1773 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1774 AddMenuEntry(&MainMenu, TempMenuEntry);
1775 break;
1776 case TAG_EXIT:
1777 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1778 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1779 AddMenuEntry(&MainMenu, TempMenuEntry);
1780 break;
1781 case TAG_SHELL:
1782 j = 0;
1783 MyFreePool(FileName);
1784 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1785 if (FileExists(SelfRootDir, FileName)) {
1786 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
1787 'S', FALSE);
1788 }
1789 } // while
1790 break;
1791 case TAG_GPTSYNC:
1792 MyFreePool(FileName);
1793 FileName = NULL;
1794 MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1795 if (FileExists(SelfRootDir, FileName)) {
1796 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1797 }
1798 break;
1799 case TAG_APPLE_RECOVERY:
1800 MyFreePool(FileName);
1801 FileName = NULL;
1802 MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1803 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1804 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
1805 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
1806 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
1807 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
1808 }
1809 } // for
1810 break;
1811 case TAG_MOK_TOOL:
1812 j = 0;
1813 MyFreePool(FileName);
1814 while ((FileName = FindCommaDelimited(MOK_NAMES, j++)) != NULL) {
1815 if (FileExists(SelfRootDir, FileName)) {
1816 SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
1817 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
1818 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
1819 }
1820 } // while
1821 if (FileExists(SelfDir, L"MokManager.efi")) {
1822 MyFreePool(FileName);
1823 FileName = StrDuplicate(SelfDirPath);
1824 MergeStrings(&FileName, L"\\MokManager.efi", 0);
1825 SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
1826 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
1827 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
1828 }
1829 break;
1830 } // switch()
1831 MyFreePool(FileName);
1832 FileName = NULL;
1833 } // for
1834 } // static VOID ScanForTools
1835
1836 // Rescan for boot loaders
1837 VOID RescanAll(VOID) {
1838 EG_PIXEL BGColor;
1839
1840 BGColor.b = 255;
1841 BGColor.g = 175;
1842 BGColor.r = 100;
1843 BGColor.a = 0;
1844 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
1845 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
1846 MainMenu.Entries = NULL;
1847 MainMenu.EntryCount = 0;
1848 ReadConfig();
1849 ConnectAllDriversToAllControllers();
1850 ScanVolumes();
1851 ScanForBootloaders();
1852 ScanForTools();
1853 SetupScreen();
1854 } // VOID RescanAll()
1855
1856 #ifndef __MAKEWITH_GNUEFI
1857
1858 // Minimal initialization function
1859 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
1860 gST = SystemTable;
1861 // gImageHandle = ImageHandle;
1862 gBS = SystemTable->BootServices;
1863 // gRS = SystemTable->RuntimeServices;
1864 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
1865 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
1866
1867 InitializeConsoleSim();
1868 }
1869
1870 #endif
1871
1872 //
1873 // main entry point
1874 //
1875 EFI_STATUS
1876 EFIAPI
1877 efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
1878 {
1879 EFI_STATUS Status;
1880 BOOLEAN MainLoopRunning = TRUE;
1881 REFIT_MENU_ENTRY *ChosenEntry;
1882 UINTN MenuExit, i;
1883 CHAR16 *Selection;
1884 EG_PIXEL BGColor;
1885
1886 // bootstrap
1887 InitializeLib(ImageHandle, SystemTable);
1888 InitScreen();
1889 Status = InitRefitLib(ImageHandle);
1890 if (EFI_ERROR(Status))
1891 return Status;
1892
1893 // read configuration
1894 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
1895 FindLegacyBootType();
1896 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
1897 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
1898 ReadConfig();
1899 WarnIfLegacyProblems();
1900 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
1901
1902 // disable EFI watchdog timer
1903 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
1904
1905 // further bootstrap (now with config available)
1906 SetupScreen();
1907 ScanVolumes();
1908 LoadDrivers();
1909 ScanForBootloaders();
1910 ScanForTools();
1911
1912 if (GlobalConfig.ScanDelay > 0) {
1913 BGColor.b = 255;
1914 BGColor.g = 175;
1915 BGColor.r = 100;
1916 BGColor.a = 0;
1917 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
1918 for (i = 0; i < GlobalConfig.ScanDelay; i++)
1919 refit_call1_wrapper(BS->Stall, 1000000);
1920 RescanAll();
1921 } // if
1922
1923 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
1924 while (MainLoopRunning) {
1925 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
1926
1927 // The Escape key triggers a re-scan operation....
1928 if (MenuExit == MENU_EXIT_ESCAPE) {
1929 RescanAll();
1930 continue;
1931 }
1932
1933 switch (ChosenEntry->Tag) {
1934
1935 case TAG_REBOOT: // Reboot
1936 TerminateScreen();
1937 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1938 MainLoopRunning = FALSE; // just in case we get this far
1939 break;
1940
1941 case TAG_SHUTDOWN: // Shut Down
1942 TerminateScreen();
1943 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1944 MainLoopRunning = FALSE; // just in case we get this far
1945 break;
1946
1947 case TAG_ABOUT: // About rEFInd
1948 AboutrEFInd();
1949 break;
1950
1951 case TAG_LOADER: // Boot OS via .EFI loader
1952 StartLoader((LOADER_ENTRY *)ChosenEntry);
1953 break;
1954
1955 case TAG_LEGACY: // Boot legacy OS
1956 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
1957 break;
1958
1959 #ifdef __MAKEWITH_TIANO
1960 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
1961 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
1962 break;
1963 #endif // __MAKEWITH_TIANO
1964
1965 case TAG_TOOL: // Start a EFI tool
1966 StartTool((LOADER_ENTRY *)ChosenEntry);
1967 break;
1968
1969 case TAG_EXIT: // Terminate rEFInd
1970 BeginTextScreen(L" ");
1971 return EFI_SUCCESS;
1972 break;
1973
1974 } // switch()
1975 MyFreePool(Selection);
1976 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
1977 } // while()
1978
1979 // If we end up here, things have gone wrong. Try to reboot, and if that
1980 // fails, go into an endless loop.
1981 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1982 EndlessIdleLoop();
1983
1984 return EFI_SUCCESS;
1985 } /* efi_main() */