]> code.delx.au - refind/blob - refind/main.c
669cdb83fe873d95084218e8fad4b9f9f468d6ae
[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, 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,
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.3.2");
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 } // not single-user
572
573 // check for Apple hardware diagnostics
574 StrCpy(DiagsFileName, L"System\\Library\\CoreServices\\.diagnostics\\diags.efi");
575 if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) {
576 SubEntry = InitializeLoaderEntry(Entry);
577 if (SubEntry != NULL) {
578 SubEntry->me.Title = L"Run Apple Hardware Test";
579 MyFreePool(SubEntry->LoaderPath);
580 SubEntry->LoaderPath = StrDuplicate(DiagsFileName);
581 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
582 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
583 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
584 } // if
585 } // if diagnostics entry found
586
587 } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders
588 File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume);
589 if (File != NULL) {
590 InitrdName = FindInitrd(Entry->LoaderPath, Volume);
591 TokenCount = ReadTokenLine(File, &TokenList);
592 // first entry requires special processing, since it was initially set
593 // up with a default title but correct options by InitializeSubScreen(),
594 // earlier....
595 if ((SubScreen->Entries != NULL) && (SubScreen->Entries[0] != NULL)) {
596 MyFreePool(SubScreen->Entries[0]->Title);
597 SubScreen->Entries[0]->Title = StrDuplicate(TokenList[0]);
598 } // if
599 FreeTokenLine(&TokenList, &TokenCount);
600 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
601 SubEntry = InitializeLoaderEntry(Entry);
602 SubEntry->me.Title = StrDuplicate(TokenList[0]);
603 MyFreePool(SubEntry->LoadOptions);
604 SubEntry->LoadOptions = AddInitrdToOptions(TokenList[1], InitrdName);
605 FreeTokenLine(&TokenList, &TokenCount);
606 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
607 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
608 } // while
609 MyFreePool(InitrdName);
610 MyFreePool(File);
611 } // if Linux options file exists
612
613 } else if (Entry->OSType == 'E') { // entries for ELILO
614 SubEntry = InitializeLoaderEntry(Entry);
615 if (SubEntry != NULL) {
616 SubEntry->me.Title = L"Run ELILO in interactive mode";
617 SubEntry->LoadOptions = L"-p";
618 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
619 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
620 }
621
622 SubEntry = InitializeLoaderEntry(Entry);
623 if (SubEntry != NULL) {
624 SubEntry->me.Title = L"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
625 SubEntry->UseGraphicsMode = TRUE;
626 SubEntry->LoadOptions = L"-d 0 i17";
627 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
628 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
629 }
630
631 SubEntry = InitializeLoaderEntry(Entry);
632 if (SubEntry != NULL) {
633 SubEntry->me.Title = L"Boot Linux for a 20\" iMac (*)";
634 SubEntry->UseGraphicsMode = TRUE;
635 SubEntry->LoadOptions = L"-d 0 i20";
636 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
637 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
638 }
639
640 SubEntry = InitializeLoaderEntry(Entry);
641 if (SubEntry != NULL) {
642 SubEntry->me.Title = L"Boot Linux for a Mac Mini (*)";
643 SubEntry->UseGraphicsMode = TRUE;
644 SubEntry->LoadOptions = L"-d 0 mini";
645 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
646 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
647 }
648
649 AddMenuInfoLine(SubScreen, L"NOTE: This is an example. Entries");
650 AddMenuInfoLine(SubScreen, L"marked with (*) may not work.");
651
652 } else if (Entry->OSType == 'X') { // entries for xom.efi
653 // by default, skip the built-in selection and boot from hard disk only
654 Entry->LoadOptions = L"-s -h";
655
656 SubEntry = InitializeLoaderEntry(Entry);
657 if (SubEntry != NULL) {
658 SubEntry->me.Title = L"Boot Windows from Hard Disk";
659 SubEntry->LoadOptions = L"-s -h";
660 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
661 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
662 }
663
664 SubEntry = InitializeLoaderEntry(Entry);
665 if (SubEntry != NULL) {
666 SubEntry->me.Title = L"Boot Windows from CD-ROM";
667 SubEntry->LoadOptions = L"-s -c";
668 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
669 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
670 }
671
672 SubEntry = InitializeLoaderEntry(Entry);
673 if (SubEntry != NULL) {
674 SubEntry->me.Title = L"Run XOM in text mode";
675 SubEntry->UseGraphicsMode = FALSE;
676 SubEntry->LoadOptions = L"-v";
677 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
678 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
679 }
680 } // entries for xom.efi
681 AddMenuEntry(SubScreen, &MenuEntryReturn);
682 Entry->me.SubScreen = SubScreen;
683 } // VOID GenerateSubScreen()
684
685 // Returns options for a Linux kernel. Reads them from an options file in the
686 // kernel's directory; and if present, adds an initrd= option for an initial
687 // RAM disk file with the same version number as the kernel file.
688 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
689 CHAR16 *Options = NULL, *InitrdName, *FullOptions = NULL;
690
691 Options = GetFirstOptionsFromFile(LoaderPath, Volume);
692 InitrdName = FindInitrd(LoaderPath, Volume);
693 FullOptions = AddInitrdToOptions(Options, InitrdName);
694
695 MyFreePool(Options);
696 MyFreePool(InitrdName);
697 return (FullOptions);
698 } // static CHAR16 * GetMainLinuxOptions()
699
700 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
701 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
702 // that will (with luck) work fairly automatically.
703 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, REFIT_VOLUME *Volume) {
704 CHAR16 IconFileName[256];
705 CHAR16 *FileName, *PathOnly, *OSIconName = NULL, *Temp, *SubString;
706 CHAR16 ShortcutLetter = 0;
707 UINTN i, Length;
708
709 FileName = Basename(LoaderPath);
710 PathOnly = FindPath(LoaderPath);
711
712 // locate a custom icon for the loader
713 // Anything found here takes precedence over the "hints" in the OSIconName variable
714 StrCpy(IconFileName, LoaderPath);
715 ReplaceEfiExtension(IconFileName, L".icns");
716 if (FileExists(Volume->RootDir, IconFileName)) {
717 Entry->me.Image = LoadIcns(Volume->RootDir, IconFileName, 128);
718 } else if ((StrLen(PathOnly) == 0) && (Volume->VolIconImage != NULL)) {
719 Entry->me.Image = Volume->VolIconImage;
720 } // icon matched to loader or volume
721
722 // Begin creating icon "hints" by using last part of directory path leading
723 // to the loader
724 Temp = FindLastDirName(LoaderPath);
725 MergeStrings(&OSIconName, Temp, L',');
726 MyFreePool(Temp);
727 Temp = NULL;
728 if (OSIconName != NULL) {
729 ShortcutLetter = OSIconName[0];
730 }
731
732 // Add every "word" in the volume label, delimited by spaces, dashes (-), or
733 // underscores (_), to the list of hints to be used in searching for OS
734 // icons.
735 if ((Volume->VolName) && (StrLen(Volume->VolName) > 0)) {
736 Temp = SubString = StrDuplicate(Volume->VolName);
737 if (Temp != NULL) {
738 Length = StrLen(Temp);
739 for (i = 0; i < Length; i++) {
740 if ((Temp[i] == L' ') || (Temp[i] == L'_') || (Temp[i] == L'-')) {
741 Temp[i] = 0;
742 if (StrLen(SubString) > 0)
743 MergeStrings(&OSIconName, SubString, L',');
744 SubString = Temp + i + 1;
745 } // if
746 } // for
747 MergeStrings(&OSIconName, SubString, L',');
748 MyFreePool(Temp);
749 } // if
750 } // if
751
752 // detect specific loaders
753 if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) {
754 MergeStrings(&OSIconName, L"linux", L',');
755 Entry->OSType = 'L';
756 if (ShortcutLetter == 0)
757 ShortcutLetter = 'L';
758 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
759 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
760 } else if (StriSubCmp(L"refit", LoaderPath)) {
761 MergeStrings(&OSIconName, L"refit", L',');
762 Entry->OSType = 'R';
763 ShortcutLetter = 'R';
764 } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) {
765 if (Volume->VolIconImage != NULL) { // custom icon file found
766 Entry->me.Image = Volume->VolIconImage;
767 }
768 MergeStrings(&OSIconName, L"mac", L',');
769 Entry->OSType = 'M';
770 ShortcutLetter = 'M';
771 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
772 } else if (StriCmp(FileName, L"diags.efi") == 0) {
773 MergeStrings(&OSIconName, L"hwtest", L',');
774 } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0 || StriSubCmp(L"elilo", FileName)) {
775 MergeStrings(&OSIconName, L"elilo,linux", L',');
776 Entry->OSType = 'E';
777 if (ShortcutLetter == 0)
778 ShortcutLetter = 'L';
779 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
780 } else if (StriSubCmp(L"grub", FileName)) {
781 Entry->OSType = 'G';
782 ShortcutLetter = 'G';
783 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
784 } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
785 StriCmp(FileName, L"bootmgr.efi") == 0 ||
786 StriCmp(FileName, L"bootmgfw.efi") == 0) {
787 MergeStrings(&OSIconName, L"win", L',');
788 Entry->OSType = 'W';
789 ShortcutLetter = 'W';
790 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
791 } else if (StriCmp(FileName, L"xom.efi") == 0) {
792 MergeStrings(&OSIconName, L"xom,win", L',');
793 Entry->UseGraphicsMode = TRUE;
794 Entry->OSType = 'X';
795 ShortcutLetter = 'W';
796 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
797 }
798
799 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
800 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
801 Entry->me.ShortcutLetter = ShortcutLetter;
802 if (Entry->me.Image == NULL)
803 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
804 MyFreePool(PathOnly);
805 } // VOID SetLoaderDefaults()
806
807 // Add a specified EFI boot loader to the list, using automatic settings
808 // for icons, options, etc.
809 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
810 LOADER_ENTRY *Entry;
811
812 CleanUpPathNameSlashes(LoaderPath);
813 Entry = InitializeLoaderEntry(NULL);
814 if (Entry != NULL) {
815 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
816 Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
817 SPrint(Entry->me.Title, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
818 Entry->me.Row = 0;
819 Entry->me.BadgeImage = Volume->VolBadgeImage;
820 if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
821 Entry->LoaderPath = StrDuplicate(L"\\");
822 } else {
823 Entry->LoaderPath = NULL;
824 }
825 MergeStrings(&(Entry->LoaderPath), LoaderPath, 0);
826 Entry->VolName = Volume->VolName;
827 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
828 SetLoaderDefaults(Entry, LoaderPath, Volume);
829 GenerateSubScreen(Entry, Volume);
830 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
831 }
832
833 return(Entry);
834 } // LOADER_ENTRY * AddLoaderEntry()
835
836 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
837 // (Time1 == Time2). Precision is only to the nearest second; since
838 // this is used for sorting boot loader entries, differences smaller
839 // than this are likely to be meaningless (and unlikely!).
840 INTN TimeComp(IN EFI_TIME *Time1, IN EFI_TIME *Time2) {
841 INT64 Time1InSeconds, Time2InSeconds;
842
843 // Following values are overestimates; I'm assuming 31 days in every month.
844 // This is fine for the purpose of this function, which is limited
845 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
846 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
847 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
848 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
849 if (Time1InSeconds < Time2InSeconds)
850 return (-1);
851 else if (Time1InSeconds > Time2InSeconds)
852 return (1);
853
854 return 0;
855 } // INTN TimeComp()
856
857 // Adds a loader list element, keeping it sorted by date. Returns the new
858 // first element (the one with the most recent date).
859 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
860 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
861
862 LatestEntry = CurrentEntry = LoaderList;
863 if (LoaderList == NULL) {
864 LatestEntry = NewEntry;
865 } else {
866 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
867 PrevEntry = CurrentEntry;
868 CurrentEntry = CurrentEntry->NextEntry;
869 } // while
870 NewEntry->NextEntry = CurrentEntry;
871 if (PrevEntry == NULL) {
872 LatestEntry = NewEntry;
873 } else {
874 PrevEntry->NextEntry = NewEntry;
875 } // if/else
876 } // if/else
877 return (LatestEntry);
878 } // static VOID AddLoaderListEntry()
879
880 // Delete the LOADER_LIST linked list
881 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
882 struct LOADER_LIST *Temp;
883
884 while (LoaderList != NULL) {
885 Temp = LoaderList;
886 LoaderList = LoaderList->NextEntry;
887 MyFreePool(Temp->FileName);
888 MyFreePool(Temp);
889 } // while
890 } // static VOID CleanUpLoaderList()
891
892 // Returns FALSE if the specified file/volume matches the GlobalConfig.DontScanDirs
893 // or GlobalConfig.DontScanVolumes specification, or if Path points to a volume
894 // other than the one specified by Volume. Returns TRUE if none of these conditions
895 // is met -- that is, if the path is eligible for scanning. Also reduces *Path to a
896 // path alone, with no volume specification.
897 static BOOLEAN ShouldScan(REFIT_VOLUME *Volume, CHAR16 *Path) {
898 CHAR16 *VolName = NULL, *DontScanDir;
899 UINTN i = 0, VolNum;
900 BOOLEAN ScanIt = TRUE;
901
902 if (IsIn(Volume->VolName, GlobalConfig.DontScanVolumes))
903 return FALSE;
904
905 while ((DontScanDir = FindCommaDelimited(GlobalConfig.DontScanDirs, i++)) && ScanIt) {
906 SplitVolumeAndFilename(&DontScanDir, &VolName);
907 CleanUpPathNameSlashes(DontScanDir);
908 if (VolName != NULL) {
909 if ((StriCmp(VolName, Volume->VolName) == 0) && (StriCmp(DontScanDir, Path) == 0))
910 ScanIt = FALSE;
911 if ((StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') && (VolName[2] >= L'0') && (VolName[2] <= L'9')) {
912 VolNum = Atoi(VolName + 2);
913 if ((VolNum == Volume->VolNumber) && (StriCmp(DontScanDir, Path) == 0))
914 ScanIt = FALSE;
915 }
916 } else {
917 if (StriCmp(DontScanDir, Path) == 0)
918 ScanIt = FALSE;
919 }
920 MyFreePool(DontScanDir);
921 DontScanDir = NULL;
922 }
923 return ScanIt;
924 } // BOOLEAN ShouldScan()
925
926 // Scan an individual directory for EFI boot loader files and, if found,
927 // add them to the list. Sorts the entries within the loader directory
928 // so that the most recent one appears first in the list.
929 static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
930 {
931 EFI_STATUS Status;
932 REFIT_DIR_ITER DirIter;
933 EFI_FILE_INFO *DirEntry;
934 CHAR16 FileName[256], *Extension;
935 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
936
937 if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && (Volume->DeviceHandle != SelfVolume->DeviceHandle)) ||
938 (StriCmp(Path, SelfDirPath) != 0)) &&
939 (ShouldScan(Volume, Path))) {
940 // look through contents of the directory
941 DirIterOpen(Volume->RootDir, Path, &DirIter);
942 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
943 Extension = FindExtension(DirEntry->FileName);
944 if (DirEntry->FileName[0] == '.' ||
945 StriCmp(Extension, L".icns") == 0 ||
946 StriSubCmp(L"shell", DirEntry->FileName) ||
947 IsIn(DirEntry->FileName, GlobalConfig.DontScanFiles))
948 continue; // skip this
949
950 if (Path)
951 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
952 else
953 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
954 CleanUpPathNameSlashes(FileName);
955 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
956 if (NewLoader != NULL) {
957 NewLoader->FileName = StrDuplicate(FileName);
958 NewLoader->TimeStamp = DirEntry->ModificationTime;
959 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
960 } // if
961 MyFreePool(Extension);
962 } // while
963 NewLoader = LoaderList;
964 while (NewLoader != NULL) {
965 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
966 NewLoader = NewLoader->NextEntry;
967 } // while
968 CleanUpLoaderList(LoaderList);
969 Status = DirIterClose(&DirIter);
970 if (Status != EFI_NOT_FOUND) {
971 if (Path)
972 SPrint(FileName, 255, L"while scanning the %s directory", Path);
973 else
974 StrCpy(FileName, L"while scanning the root directory");
975 CheckError(Status, FileName);
976 } // if (Status != EFI_NOT_FOUND)
977 } // if not scanning our own directory
978 } /* static VOID ScanLoaderDir() */
979
980 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
981 EFI_STATUS Status;
982 REFIT_DIR_ITER EfiDirIter;
983 EFI_FILE_INFO *EfiDirEntry;
984 CHAR16 FileName[256], *Directory, *MatchPatterns, *VolName = NULL;
985 UINTN i, Length, VolNum;
986
987 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
988 if (GlobalConfig.ScanAllLinux)
989 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
990
991 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
992 // check for Mac OS X boot loader
993 if (!IsIn(L"System\\Library\\CoreServices", GlobalConfig.DontScanDirs)) {
994 StrCpy(FileName, MACOSX_LOADER_PATH);
995 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
996 AddLoaderEntry(FileName, L"Mac OS X", Volume);
997 }
998
999 // check for XOM
1000 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
1001 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"boot.efi", GlobalConfig.DontScanFiles)) {
1002 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
1003 }
1004 } // if Mac directory not in GlobalConfig.DontScanDirs list
1005
1006 // check for Microsoft boot loader/menu
1007 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
1008 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"EFI\\Microsoft\\Boot", GlobalConfig.DontScanDirs) &&
1009 !IsIn(L"bootmgfw.efi", GlobalConfig.DontScanFiles)) {
1010 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
1011 }
1012
1013 // scan the root directory for EFI executables
1014 ScanLoaderDir(Volume, L"\\", MatchPatterns);
1015
1016 // scan subdirectories of the EFI directory (as per the standard)
1017 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
1018 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
1019 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
1020 continue; // skip this, doesn't contain boot loaders
1021 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
1022 ScanLoaderDir(Volume, FileName, MatchPatterns);
1023 } // while()
1024 Status = DirIterClose(&EfiDirIter);
1025 if (Status != EFI_NOT_FOUND)
1026 CheckError(Status, L"while scanning the EFI directory");
1027
1028 // Scan user-specified (or additional default) directories....
1029 i = 0;
1030 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
1031 VolNum = VOL_DONTSCAN;
1032 SplitVolumeAndFilename(&Directory, &VolName);
1033 CleanUpPathNameSlashes(Directory);
1034 Length = StrLen(Directory);
1035 if ((Length > 0) && (StrLen(VolName) > 2) && (VolName[0] == L'f') && (VolName[1] == L's') &&
1036 (VolName[2] >= L'0') && (VolName[2] <= L'9'))
1037 VolNum = Atoi(VolName + 2);
1038 if ((Length > 0) && ((VolName == NULL) || (StriCmp(VolName, Volume->VolName) == 0) || (Volume->VolNumber == VolNum)))
1039 ScanLoaderDir(Volume, Directory, MatchPatterns);
1040 MyFreePool(Directory);
1041 MyFreePool(VolName);
1042 } // while
1043 } // if
1044 } // static VOID ScanEfiFiles()
1045
1046 // Scan internal disks for valid EFI boot loaders....
1047 static VOID ScanInternal(VOID) {
1048 UINTN VolumeIndex;
1049
1050 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1051 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
1052 ScanEfiFiles(Volumes[VolumeIndex]);
1053 }
1054 } // for
1055 } // static VOID ScanInternal()
1056
1057 // Scan external disks for valid EFI boot loaders....
1058 static VOID ScanExternal(VOID) {
1059 UINTN VolumeIndex;
1060
1061 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1062 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
1063 ScanEfiFiles(Volumes[VolumeIndex]);
1064 }
1065 } // for
1066 } // static VOID ScanExternal()
1067
1068 // Scan internal disks for valid EFI boot loaders....
1069 static VOID ScanOptical(VOID) {
1070 UINTN VolumeIndex;
1071
1072 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1073 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
1074 ScanEfiFiles(Volumes[VolumeIndex]);
1075 }
1076 } // for
1077 } // static VOID ScanOptical()
1078
1079 //
1080 // legacy boot functions
1081 //
1082
1083 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
1084 {
1085 EFI_STATUS Status;
1086 UINT8 SectorBuffer[512];
1087 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
1088 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
1089 UINTN LogicalPartitionIndex = 4;
1090 UINTN i;
1091 BOOLEAN HaveBootCode;
1092
1093 // read MBR
1094 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1095 if (EFI_ERROR(Status))
1096 return Status;
1097 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1098 return EFI_NOT_FOUND; // safety measure #1
1099
1100 // add boot code if necessary
1101 HaveBootCode = FALSE;
1102 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
1103 if (SectorBuffer[i] != 0) {
1104 HaveBootCode = TRUE;
1105 break;
1106 }
1107 }
1108 if (!HaveBootCode) {
1109 // no boot code found in the MBR, add the syslinux MBR code
1110 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
1111 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
1112 }
1113
1114 // set the partition active
1115 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1116 ExtBase = 0;
1117 for (i = 0; i < 4; i++) {
1118 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1119 return EFI_NOT_FOUND; // safety measure #2
1120 if (i == PartitionIndex)
1121 MbrTable[i].Flags = 0x80;
1122 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1123 MbrTable[i].Flags = 0x80;
1124 ExtBase = MbrTable[i].StartLBA;
1125 } else
1126 MbrTable[i].Flags = 0x00;
1127 }
1128
1129 // write MBR
1130 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1131 if (EFI_ERROR(Status))
1132 return Status;
1133
1134 if (PartitionIndex >= 4) {
1135 // we have to activate a logical partition, so walk the EMBR chain
1136
1137 // NOTE: ExtBase was set above while looking at the MBR table
1138 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1139 // read current EMBR
1140 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1141 if (EFI_ERROR(Status))
1142 return Status;
1143 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1144 return EFI_NOT_FOUND; // safety measure #3
1145
1146 // scan EMBR, set appropriate partition active
1147 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1148 NextExtCurrent = 0;
1149 for (i = 0; i < 4; i++) {
1150 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1151 return EFI_NOT_FOUND; // safety measure #4
1152 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1153 break;
1154 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1155 // link to next EMBR
1156 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1157 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1158 break;
1159 } else {
1160 // logical partition
1161 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1162 LogicalPartitionIndex++;
1163 }
1164 }
1165
1166 // write current EMBR
1167 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1168 if (EFI_ERROR(Status))
1169 return Status;
1170
1171 if (PartitionIndex < LogicalPartitionIndex)
1172 break; // stop the loop, no need to touch further EMBRs
1173 }
1174
1175 }
1176
1177 return EFI_SUCCESS;
1178 } /* static EFI_STATUS ActivateMbrPartition() */
1179
1180 // early 2006 Core Duo / Core Solo models
1181 static UINT8 LegacyLoaderDevicePath1Data[] = {
1182 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1183 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1184 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1185 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1186 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1187 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1188 };
1189 // mid-2006 Mac Pro (and probably other Core 2 models)
1190 static UINT8 LegacyLoaderDevicePath2Data[] = {
1191 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1192 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1193 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1194 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1195 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1196 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1197 };
1198 // mid-2007 MBP ("Santa Rosa" based models)
1199 static UINT8 LegacyLoaderDevicePath3Data[] = {
1200 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1201 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1202 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1203 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1204 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1205 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1206 };
1207 // early-2008 MBA
1208 static UINT8 LegacyLoaderDevicePath4Data[] = {
1209 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1210 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1211 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1212 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1213 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1214 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1215 };
1216 // late-2008 MB/MBP (NVidia chipset)
1217 static UINT8 LegacyLoaderDevicePath5Data[] = {
1218 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1219 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1220 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1221 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1222 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1223 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1224 };
1225
1226 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1227 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1228 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1229 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1230 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1231 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1232 NULL
1233 };
1234
1235 #define MAX_DISCOVERED_PATHS (16)
1236
1237 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1238 {
1239 EFI_STATUS Status;
1240 EG_IMAGE *BootLogoImage;
1241 UINTN ErrorInStep = 0;
1242 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1243
1244 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
1245
1246 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1247 if (BootLogoImage != NULL)
1248 BltImageAlpha(BootLogoImage,
1249 (UGAWidth - BootLogoImage->Width ) >> 1,
1250 (UGAHeight - BootLogoImage->Height) >> 1,
1251 &StdBackgroundPixel);
1252
1253 if (Entry->Volume->IsMbrPartition) {
1254 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1255 }
1256
1257 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1258
1259 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", 0, &ErrorInStep, TRUE);
1260 if (Status == EFI_NOT_FOUND) {
1261 if (ErrorInStep == 1) {
1262 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1263 } else if (ErrorInStep == 3) {
1264 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1265 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1266 }
1267 }
1268 FinishExternalScreen();
1269 } /* static VOID StartLegacy() */
1270
1271 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
1272 #ifdef __MAKEWITH_TIANO
1273 static VOID StartLegacyUEFI(IN LEGACY_ENTRY *Entry)
1274 {
1275 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
1276
1277 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
1278 BdsLibDoLegacyBoot(Entry->BdsOption);
1279
1280 // If we get here, it means that there was a failure....
1281 Print(L"Failure booting legacy (BIOS) OS.");
1282 PauseForKey();
1283 FinishExternalScreen();
1284 } // static VOID StartLegacyUEFI()
1285 #endif // __MAKEWITH_TIANO
1286
1287 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1288 {
1289 LEGACY_ENTRY *Entry, *SubEntry;
1290 REFIT_MENU_SCREEN *SubScreen;
1291 CHAR16 *VolDesc;
1292 CHAR16 ShortcutLetter = 0;
1293
1294 if (LoaderTitle == NULL) {
1295 if (Volume->OSName != NULL) {
1296 LoaderTitle = Volume->OSName;
1297 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1298 ShortcutLetter = LoaderTitle[0];
1299 } else
1300 LoaderTitle = L"Legacy OS";
1301 }
1302 if (Volume->VolName != NULL)
1303 VolDesc = Volume->VolName;
1304 else
1305 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1306
1307 // prepare the menu entry
1308 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1309 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1310 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1311 Entry->me.Tag = TAG_LEGACY;
1312 Entry->me.Row = 0;
1313 Entry->me.ShortcutLetter = ShortcutLetter;
1314 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1315 Entry->me.BadgeImage = Volume->VolBadgeImage;
1316 Entry->Volume = Volume;
1317 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1318 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1319 Entry->Enabled = TRUE;
1320
1321 // create the submenu
1322 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1323 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1324 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1325 SubScreen->TitleImage = Entry->me.Image;
1326 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1327 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1328 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1329 } else {
1330 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1331 } // if/else
1332
1333 // default entry
1334 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1335 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1336 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1337 SubEntry->me.Tag = TAG_LEGACY;
1338 SubEntry->Volume = Entry->Volume;
1339 SubEntry->LoadOptions = Entry->LoadOptions;
1340 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1341
1342 AddMenuEntry(SubScreen, &MenuEntryReturn);
1343 Entry->me.SubScreen = SubScreen;
1344 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1345 return Entry;
1346 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1347
1348
1349 #ifdef __MAKEWITH_GNUEFI
1350 static VOID ScanLegacyUEFI(IN UINTN DiskType){}
1351 #else
1352 // default volume badge icon based on disk kind
1353 static EG_IMAGE * GetDiskBadge(IN UINTN DiskType) {
1354 EG_IMAGE * Badge = NULL;
1355
1356 switch (DiskType) {
1357 case BBS_HARDDISK:
1358 Badge = BuiltinIcon(BUILTIN_ICON_VOL_INTERNAL);
1359 break;
1360 case BBS_USB:
1361 Badge = BuiltinIcon(BUILTIN_ICON_VOL_EXTERNAL);
1362 break;
1363 case BBS_CDROM:
1364 Badge = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL);
1365 break;
1366 } // switch()
1367 return Badge;
1368 } // static EG_IMAGE * GetDiskBadge()
1369
1370 /**
1371 Create a rEFInd boot option from a Legacy BIOS protocol option.
1372 */
1373 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
1374 {
1375 LEGACY_ENTRY *Entry, *SubEntry;
1376 REFIT_MENU_SCREEN *SubScreen;
1377 CHAR16 ShortcutLetter = 0;
1378 CHAR16 *LegacyDescription = BdsOption->Description;
1379
1380 // ScanVolume(Volume);
1381
1382 // prepare the menu entry
1383 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1384 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1385 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
1386 Entry->me.Tag = TAG_LEGACY_UEFI;
1387 Entry->me.Row = 0;
1388 Entry->me.ShortcutLetter = ShortcutLetter;
1389 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
1390 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
1391 ((DiskType == BBS_USB) ? L"USB" : L"HD");
1392 Entry->me.BadgeImage = GetDiskBadge(DiskType);
1393 // Entry->me.BadgeImage = Volume->VolBadgeImage;
1394 Entry->BdsOption = BdsOption;
1395 Entry->Enabled = TRUE;
1396
1397 // create the submenu
1398 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1399 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1400 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
1401 SubScreen->TitleImage = Entry->me.Image;
1402 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
1403 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
1404 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
1405 } else {
1406 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
1407 } // if/else
1408
1409 // default entry
1410 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1411 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1412 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
1413 SubEntry->me.Tag = TAG_LEGACY_UEFI;
1414 Entry->BdsOption = BdsOption;
1415 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1416
1417 AddMenuEntry(SubScreen, &MenuEntryReturn);
1418 Entry->me.SubScreen = SubScreen;
1419 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1420 return Entry;
1421 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
1422
1423 /**
1424 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
1425 In testing, protocol has not been implemented on Macs but has been
1426 implemented on several Dell PCs and an ASUS motherboard.
1427 Restricts output to disks of the specified DiskType.
1428 */
1429 static VOID ScanLegacyUEFI(IN UINTN DiskType)
1430 {
1431 EFI_STATUS Status;
1432 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1433 UINT16 *BootOrder = NULL;
1434 UINTN Index = 0;
1435 CHAR16 BootOption[10];
1436 UINTN BootOrderSize = 0;
1437 CHAR16 Buffer[20];
1438 BDS_COMMON_OPTION *BdsOption;
1439 LIST_ENTRY TempList;
1440 BBS_BBS_DEVICE_PATH * BbsDevicePath = NULL;
1441
1442 InitializeListHead (&TempList);
1443 ZeroMem (Buffer, sizeof (Buffer));
1444
1445 // If LegacyBios protocol is not implemented on this platform, then
1446 //we do not support this type of legacy boot on this machine.
1447 Status = gBS->LocateProtocol(&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1448 if (EFI_ERROR (Status))
1449 return;
1450
1451 // Grab the boot order
1452 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
1453 if (BootOrder == NULL) {
1454 BootOrderSize = 0;
1455 }
1456
1457 Index = 0;
1458 while (Index < BootOrderSize / sizeof (UINT16))
1459 {
1460 // Grab each boot option variable from the boot order, and convert
1461 // the variable into a BDS boot option
1462 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
1463 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
1464
1465 if (BdsOption != NULL) {
1466 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
1467
1468 // Only add the entry if it is of a requested type (e.g. USB, HD)
1469
1470 // Two checks necessary because some systems return EFI boot loaders
1471 // with a DeviceType value that would inappropriately include them
1472 // as legacy loaders....
1473 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
1474 AddLegacyEntryUEFI(BdsOption, BbsDevicePath->DeviceType);
1475 }
1476 }
1477 Index++;
1478 }
1479 } /* static VOID ScanLegacyUEFI() */
1480 #endif // __MAKEWITH_GNUEFI
1481
1482 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1483 UINTN VolumeIndex2;
1484 BOOLEAN ShowVolume, HideIfOthersFound;
1485
1486 ShowVolume = FALSE;
1487 HideIfOthersFound = FALSE;
1488 if (Volume->IsAppleLegacy) {
1489 ShowVolume = TRUE;
1490 HideIfOthersFound = TRUE;
1491 } else if (Volume->HasBootCode) {
1492 ShowVolume = TRUE;
1493 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1494 Volume->BlockIOOffset == 0 &&
1495 Volume->OSName == NULL)
1496 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1497 HideIfOthersFound = TRUE;
1498 }
1499 if (HideIfOthersFound) {
1500 // check for other bootable entries on the same disk
1501 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1502 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1503 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1504 ShowVolume = FALSE;
1505 }
1506 }
1507
1508 if (ShowVolume)
1509 AddLegacyEntry(NULL, Volume);
1510 } // static VOID ScanLegacyVolume()
1511
1512 // Scan attached optical discs for legacy (BIOS) boot code
1513 // and add anything found to the list....
1514 static VOID ScanLegacyDisc(VOID)
1515 {
1516 UINTN VolumeIndex;
1517 REFIT_VOLUME *Volume;
1518
1519 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1520 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1521 Volume = Volumes[VolumeIndex];
1522 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1523 ScanLegacyVolume(Volume, VolumeIndex);
1524 } // for
1525 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1526 ScanLegacyUEFI(BBS_CDROM);
1527 }
1528 } /* static VOID ScanLegacyDisc() */
1529
1530 // Scan internal hard disks for legacy (BIOS) boot code
1531 // and add anything found to the list....
1532 static VOID ScanLegacyInternal(VOID)
1533 {
1534 UINTN VolumeIndex;
1535 REFIT_VOLUME *Volume;
1536
1537 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1538 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1539 Volume = Volumes[VolumeIndex];
1540 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1541 ScanLegacyVolume(Volume, VolumeIndex);
1542 } // for
1543 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1544 ScanLegacyUEFI(BBS_HARDDISK);
1545 }
1546 } /* static VOID ScanLegacyInternal() */
1547
1548 // Scan external disks for legacy (BIOS) boot code
1549 // and add anything found to the list....
1550 static VOID ScanLegacyExternal(VOID)
1551 {
1552 UINTN VolumeIndex;
1553 REFIT_VOLUME *Volume;
1554
1555 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
1556 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1557 Volume = Volumes[VolumeIndex];
1558 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1559 ScanLegacyVolume(Volume, VolumeIndex);
1560 } // for
1561 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
1562 ScanLegacyUEFI(BBS_USB);
1563 }
1564 } /* static VOID ScanLegacyExternal() */
1565
1566 //
1567 // pre-boot tool functions
1568 //
1569
1570 static VOID StartTool(IN LOADER_ENTRY *Entry)
1571 {
1572 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1573 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1574 Basename(Entry->LoaderPath), Entry->OSType, NULL, TRUE);
1575 FinishExternalScreen();
1576 } /* static VOID StartTool() */
1577
1578 static LOADER_ENTRY * AddToolEntry(EFI_HANDLE DeviceHandle, IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1579 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1580 {
1581 LOADER_ENTRY *Entry;
1582 CHAR16 *TitleStr = NULL;
1583
1584 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1585
1586 MergeStrings(&TitleStr, L"Start ", 0);
1587 MergeStrings(&TitleStr, LoaderTitle, 0);
1588 Entry->me.Title = TitleStr;
1589 Entry->me.Tag = TAG_TOOL;
1590 Entry->me.Row = 1;
1591 Entry->me.ShortcutLetter = ShortcutLetter;
1592 Entry->me.Image = Image;
1593 Entry->LoaderPath = (LoaderPath) ? StrDuplicate(LoaderPath) : NULL;
1594 Entry->DevicePath = FileDevicePath(DeviceHandle, Entry->LoaderPath);
1595 Entry->UseGraphicsMode = UseGraphicsMode;
1596
1597 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1598 return Entry;
1599 } /* static LOADER_ENTRY * AddToolEntry() */
1600
1601 //
1602 // pre-boot driver functions
1603 //
1604
1605 static UINTN ScanDriverDir(IN CHAR16 *Path)
1606 {
1607 EFI_STATUS Status;
1608 REFIT_DIR_ITER DirIter;
1609 UINTN NumFound = 0;
1610 EFI_FILE_INFO *DirEntry;
1611 CHAR16 FileName[256];
1612
1613 CleanUpPathNameSlashes(Path);
1614 // look through contents of the directory
1615 DirIterOpen(SelfRootDir, Path, &DirIter);
1616 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1617 if (DirEntry->FileName[0] == '.')
1618 continue; // skip this
1619
1620 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1621 NumFound++;
1622 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1623 L"", DirEntry->FileName, DirEntry->FileName, 0, NULL, FALSE);
1624 }
1625 Status = DirIterClose(&DirIter);
1626 if (Status != EFI_NOT_FOUND) {
1627 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1628 CheckError(Status, FileName);
1629 }
1630 return (NumFound);
1631 }
1632
1633 #ifdef __MAKEWITH_GNUEFI
1634 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1635 {
1636 EFI_STATUS Status;
1637 UINTN AllHandleCount;
1638 EFI_HANDLE *AllHandleBuffer;
1639 UINTN Index;
1640 UINTN HandleCount;
1641 EFI_HANDLE *HandleBuffer;
1642 UINT32 *HandleType;
1643 UINTN HandleIndex;
1644 BOOLEAN Parent;
1645 BOOLEAN Device;
1646
1647 Status = LibLocateHandle(AllHandles,
1648 NULL,
1649 NULL,
1650 &AllHandleCount,
1651 &AllHandleBuffer);
1652 if (EFI_ERROR(Status))
1653 return Status;
1654
1655 for (Index = 0; Index < AllHandleCount; Index++) {
1656 //
1657 // Scan the handle database
1658 //
1659 Status = LibScanHandleDatabase(NULL,
1660 NULL,
1661 AllHandleBuffer[Index],
1662 NULL,
1663 &HandleCount,
1664 &HandleBuffer,
1665 &HandleType);
1666 if (EFI_ERROR (Status))
1667 goto Done;
1668
1669 Device = TRUE;
1670 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1671 Device = FALSE;
1672 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1673 Device = FALSE;
1674
1675 if (Device) {
1676 Parent = FALSE;
1677 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1678 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1679 Parent = TRUE;
1680 } // for
1681
1682 if (!Parent) {
1683 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1684 Status = refit_call4_wrapper(BS->ConnectController,
1685 AllHandleBuffer[Index],
1686 NULL,
1687 NULL,
1688 TRUE);
1689 }
1690 }
1691 }
1692
1693 MyFreePool (HandleBuffer);
1694 MyFreePool (HandleType);
1695 }
1696
1697 Done:
1698 MyFreePool (AllHandleBuffer);
1699 return Status;
1700 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1701 #else
1702 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1703 BdsLibConnectAllDriversToAllControllers();
1704 return 0;
1705 }
1706 #endif
1707
1708 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1709 // directories specified by the user in the "scan_driver_dirs" configuration
1710 // file line.
1711 static VOID LoadDrivers(VOID)
1712 {
1713 CHAR16 *Directory, *SelfDirectory;
1714 UINTN i = 0, Length, NumFound = 0;
1715
1716 // load drivers from the subdirectories of rEFInd's home directory specified
1717 // in the DRIVER_DIRS constant.
1718 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1719 SelfDirectory = SelfDirPath ? StrDuplicate(SelfDirPath) : NULL;
1720 CleanUpPathNameSlashes(SelfDirectory);
1721 MergeStrings(&SelfDirectory, Directory, L'\\');
1722 NumFound += ScanDriverDir(SelfDirectory);
1723 MyFreePool(Directory);
1724 MyFreePool(SelfDirectory);
1725 }
1726
1727 // Scan additional user-specified driver directories....
1728 i = 0;
1729 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1730 CleanUpPathNameSlashes(Directory);
1731 Length = StrLen(Directory);
1732 if (Length > 0) {
1733 NumFound += ScanDriverDir(Directory);
1734 } // if
1735 MyFreePool(Directory);
1736 } // while
1737
1738 // connect all devices
1739 if (NumFound > 0)
1740 ConnectAllDriversToAllControllers();
1741 } /* static VOID LoadDrivers() */
1742
1743 // Determine what (if any) type of legacy (BIOS) boot support is available
1744 static VOID FindLegacyBootType(VOID) {
1745 #ifdef __MAKEWITH_TIANO
1746 EFI_STATUS Status;
1747 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
1748 #endif
1749
1750 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
1751
1752 // UEFI-style legacy BIOS support is available only with the TianoCore EDK2
1753 // build environment, and then only with some EFI implementations....
1754 #ifdef __MAKEWITH_TIANO
1755 Status = gBS->LocateProtocol (&gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
1756 if (!EFI_ERROR (Status))
1757 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
1758 #endif
1759
1760 // Macs have their own system. If the firmware vendor code contains the
1761 // string "Apple", assume it's available. Note that this overrides the
1762 // UEFI type, and might yield false positives if the vendor string
1763 // contains "Apple" as part of something bigger, so this isn't 100%
1764 // perfect.
1765 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
1766 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
1767 } // static VOID FindLegacyBootType
1768
1769 // Warn the user if legacy OS scans are enabled but the firmware or this
1770 // application can't support them....
1771 static VOID WarnIfLegacyProblems() {
1772 BOOLEAN found = FALSE;
1773 UINTN i = 0;
1774
1775 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
1776 do {
1777 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c')
1778 found = TRUE;
1779 i++;
1780 } while ((i < NUM_SCAN_OPTIONS) && (!found));
1781 if (found) {
1782 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
1783 Print(L"(BIOS) boot options; however, this is not possible because ");
1784 #ifdef __MAKEWITH_TIANO
1785 Print(L"your computer lacks\n");
1786 Print(L"the necessary Compatibility Support Module (CSM) support.\n");
1787 #else
1788 Print(L"this program was\n");
1789 Print(L"compiled without the necessary support. Please visit\n");
1790 Print(L"http://www.rodsbooks.com/refind/getting.html and download and install a rEFInd\n");
1791 Print(L"binary built with the TianoCore EDK2 to enable legacy boot support.\n");
1792 #endif
1793 PauseForKey();
1794 } // if (found)
1795 } // if no legacy support
1796 } // static VOID WarnIfLegacyProblems()
1797
1798 // Locates boot loaders. NOTE: This assumes that GlobalConfig.LegacyType is set correctly.
1799 static VOID ScanForBootloaders(VOID) {
1800 UINTN i;
1801
1802 ScanVolumes();
1803
1804 // scan for loaders and tools, add them to the menu
1805 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1806 switch(GlobalConfig.ScanFor[i]) {
1807 case 'c': case 'C':
1808 ScanLegacyDisc();
1809 break;
1810 case 'h': case 'H':
1811 ScanLegacyInternal();
1812 break;
1813 case 'b': case 'B':
1814 ScanLegacyExternal();
1815 break;
1816 case 'm': case 'M':
1817 ScanUserConfigured();
1818 break;
1819 case 'e': case 'E':
1820 ScanExternal();
1821 break;
1822 case 'i': case 'I':
1823 ScanInternal();
1824 break;
1825 case 'o': case 'O':
1826 ScanOptical();
1827 break;
1828 } // switch()
1829 } // for
1830
1831 // assign shortcut keys
1832 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1833 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1834
1835 // wait for user ACK when there were errors
1836 FinishTextScreen(FALSE);
1837 } // static VOID ScanForBootloaders()
1838
1839 // Add the second-row tags containing built-in and external tools (EFI shell,
1840 // reboot, etc.)
1841 static VOID ScanForTools(VOID) {
1842 CHAR16 *FileName = NULL, Description[256];
1843 REFIT_MENU_ENTRY *TempMenuEntry;
1844 UINTN i, j, VolumeIndex;
1845
1846 for (i = 0; i < NUM_TOOLS; i++) {
1847 switch(GlobalConfig.ShowTools[i]) {
1848 case TAG_SHUTDOWN:
1849 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1850 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1851 AddMenuEntry(&MainMenu, TempMenuEntry);
1852 break;
1853 case TAG_REBOOT:
1854 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1855 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1856 AddMenuEntry(&MainMenu, TempMenuEntry);
1857 break;
1858 case TAG_ABOUT:
1859 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1860 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1861 AddMenuEntry(&MainMenu, TempMenuEntry);
1862 break;
1863 case TAG_EXIT:
1864 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1865 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1866 AddMenuEntry(&MainMenu, TempMenuEntry);
1867 break;
1868 case TAG_SHELL:
1869 j = 0;
1870 MyFreePool(FileName);
1871 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1872 if (FileExists(SelfRootDir, FileName)) {
1873 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL),
1874 'S', FALSE);
1875 }
1876 } // while
1877 break;
1878 case TAG_GPTSYNC:
1879 MyFreePool(FileName);
1880 FileName = StrDuplicate(L"\\efi\\tools\\gptsync.efi");
1881 // MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1882 if (FileExists(SelfRootDir, FileName)) {
1883 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1884 }
1885 break;
1886 case TAG_APPLE_RECOVERY:
1887 MyFreePool(FileName);
1888 FileName = StrDuplicate(L"\\com.apple.recovery.boot\\boot.efi");
1889 // MergeStrings(&FileName, L"\\com.apple.recovery.boot\\boot.efi", 0);
1890 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1891 if ((Volumes[VolumeIndex]->RootDir != NULL) && (FileExists(Volumes[VolumeIndex]->RootDir, FileName))) {
1892 SPrint(Description, 255, L"Apple Recovery on %s", Volumes[VolumeIndex]->VolName);
1893 AddToolEntry(Volumes[VolumeIndex]->DeviceHandle, FileName, Description,
1894 BuiltinIcon(BUILTIN_ICON_TOOL_APPLE_RESCUE), 'R', TRUE);
1895 }
1896 } // for
1897 break;
1898 case TAG_MOK_TOOL:
1899 j = 0;
1900 MyFreePool(FileName);
1901 while ((FileName = FindCommaDelimited(MOK_NAMES, j++)) != NULL) {
1902 if (FileExists(SelfRootDir, FileName)) {
1903 SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
1904 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
1905 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
1906 }
1907 } // while
1908 if (FileExists(SelfDir, L"MokManager.efi")) {
1909 MyFreePool(FileName);
1910 FileName = StrDuplicate(SelfDirPath);
1911 MergeStrings(&FileName, L"\\MokManager.efi", 0);
1912 SPrint(Description, 255, L"MOK Key Manager at %s", FileName);
1913 AddToolEntry(SelfLoadedImage->DeviceHandle, FileName, Description,
1914 BuiltinIcon(BUILTIN_ICON_TOOL_MOK_TOOL), 'S', FALSE);
1915 }
1916 break;
1917 } // switch()
1918 MyFreePool(FileName);
1919 FileName = NULL;
1920 } // for
1921 } // static VOID ScanForTools
1922
1923 // Rescan for boot loaders
1924 VOID RescanAll(VOID) {
1925 EG_PIXEL BGColor;
1926
1927 BGColor.b = 255;
1928 BGColor.g = 175;
1929 BGColor.r = 100;
1930 BGColor.a = 0;
1931 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
1932 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
1933 MainMenu.Entries = NULL;
1934 MainMenu.EntryCount = 0;
1935 ReadConfig(CONFIG_FILE_NAME);
1936 ConnectAllDriversToAllControllers();
1937 ScanVolumes();
1938 ScanForBootloaders();
1939 ScanForTools();
1940 SetupScreen();
1941 } // VOID RescanAll()
1942
1943 #ifdef __MAKEWITH_TIANO
1944
1945 // Minimal initialization function
1946 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
1947 gST = SystemTable;
1948 // gImageHandle = ImageHandle;
1949 gBS = SystemTable->BootServices;
1950 // gRS = SystemTable->RuntimeServices;
1951 gRT = SystemTable->RuntimeServices; // Some BDS functions need gRT to be set
1952 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
1953
1954 InitializeConsoleSim();
1955 }
1956
1957 #endif
1958
1959 // Set up our own Secure Boot extensions....
1960 // Returns TRUE on success, FALSE otherwise
1961 static BOOLEAN SecureBootSetup(VOID) {
1962 EFI_STATUS Status;
1963 BOOLEAN Success = FALSE;
1964
1965 if (secure_mode() && ShimLoaded()) {
1966 Status = security_policy_install();
1967 if (Status == EFI_SUCCESS) {
1968 Success = TRUE;
1969 } else {
1970 Print(L"Failed to install MOK Secure Boot extensions");
1971 }
1972 }
1973 return Success;
1974 } // VOID SecureBootSetup()
1975
1976 // Remove our own Secure Boot extensions....
1977 // Returns TRUE on success, FALSE otherwise
1978 static BOOLEAN SecureBootUninstall(VOID) {
1979 EFI_STATUS Status;
1980 BOOLEAN Success = TRUE;
1981
1982 if (secure_mode()) {
1983 Status = security_policy_uninstall();
1984 if (Status != EFI_SUCCESS) {
1985 Success = FALSE;
1986 BeginTextScreen(L"Secure Boot Policy Failure");
1987 Print(L"Failed to uninstall MOK Secure Boot extensions; forcing a reboot.");
1988 PauseForKey();
1989 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1990 }
1991 }
1992 return Success;
1993 } // VOID SecureBootUninstall
1994
1995 //
1996 // main entry point
1997 //
1998 EFI_STATUS
1999 EFIAPI
2000 efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
2001 {
2002 EFI_STATUS Status;
2003 BOOLEAN MainLoopRunning = TRUE;
2004 BOOLEAN MokProtocol;
2005 REFIT_MENU_ENTRY *ChosenEntry;
2006 UINTN MenuExit, i;
2007 CHAR16 *Selection;
2008 EG_PIXEL BGColor;
2009
2010 // bootstrap
2011 InitializeLib(ImageHandle, SystemTable);
2012 Status = InitRefitLib(ImageHandle);
2013 if (EFI_ERROR(Status))
2014 return Status;
2015
2016 // read configuration
2017 CopyMem(GlobalConfig.ScanFor, "ieom ", NUM_SCAN_OPTIONS);
2018 FindLegacyBootType();
2019 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC)
2020 CopyMem(GlobalConfig.ScanFor, "ihebocm ", NUM_SCAN_OPTIONS);
2021 ReadConfig(CONFIG_FILE_NAME);
2022
2023 InitScreen();
2024 WarnIfLegacyProblems();
2025 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
2026
2027 // disable EFI watchdog timer
2028 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
2029
2030 // further bootstrap (now with config available)
2031 SetupScreen();
2032 MokProtocol = SecureBootSetup();
2033 ScanVolumes();
2034 LoadDrivers();
2035 ScanForBootloaders();
2036 ScanForTools();
2037
2038 if (GlobalConfig.ScanDelay > 0) {
2039 BGColor.b = 255;
2040 BGColor.g = 175;
2041 BGColor.r = 100;
2042 BGColor.a = 0;
2043 egDisplayMessage(L"Pausing before disk scan; please wait....", &BGColor);
2044 for (i = 0; i < GlobalConfig.ScanDelay; i++)
2045 refit_call1_wrapper(BS->Stall, 1000000);
2046 RescanAll();
2047 } // if
2048
2049 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
2050 while (MainLoopRunning) {
2051 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
2052
2053 // The Escape key triggers a re-scan operation....
2054 if (MenuExit == MENU_EXIT_ESCAPE) {
2055 RescanAll();
2056 continue;
2057 }
2058
2059 switch (ChosenEntry->Tag) {
2060
2061 case TAG_REBOOT: // Reboot
2062 TerminateScreen();
2063 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2064 MainLoopRunning = FALSE; // just in case we get this far
2065 break;
2066
2067 case TAG_SHUTDOWN: // Shut Down
2068 TerminateScreen();
2069 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
2070 MainLoopRunning = FALSE; // just in case we get this far
2071 break;
2072
2073 case TAG_ABOUT: // About rEFInd
2074 AboutrEFInd();
2075 break;
2076
2077 case TAG_LOADER: // Boot OS via .EFI loader
2078 StartLoader((LOADER_ENTRY *)ChosenEntry);
2079 break;
2080
2081 case TAG_LEGACY: // Boot legacy OS
2082 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
2083 break;
2084
2085 #ifdef __MAKEWITH_TIANO
2086 case TAG_LEGACY_UEFI: // Boot a legacy OS on a non-Mac
2087 StartLegacyUEFI((LEGACY_ENTRY *)ChosenEntry);
2088 break;
2089 #endif
2090
2091 case TAG_TOOL: // Start a EFI tool
2092 StartTool((LOADER_ENTRY *)ChosenEntry);
2093 break;
2094
2095 case TAG_EXIT: // Terminate rEFInd
2096 if ((MokProtocol) && !SecureBootUninstall()) {
2097 MainLoopRunning = FALSE; // just in case we get this far
2098 } else {
2099 BeginTextScreen(L" ");
2100 return EFI_SUCCESS;
2101 }
2102 break;
2103
2104 } // switch()
2105 MyFreePool(Selection);
2106 Selection = (ChosenEntry->Title) ? StrDuplicate(ChosenEntry->Title) : NULL;
2107 } // while()
2108
2109 // If we end up here, things have gone wrong. Try to reboot, and if that
2110 // fails, go into an endless loop.
2111 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
2112 EndlessIdleLoop();
2113
2114 return EFI_SUCCESS;
2115 } /* efi_main() */