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