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