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