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