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