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