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