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