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