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