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