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