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