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