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