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