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