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