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