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