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