]> code.delx.au - refind/blob - refind/main.c
TianoCore build support; new use_graphics_for refind.conf token
[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.2.1");
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(255 * 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(255 * 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(255 * 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 }
202 // NOTE: We also include the terminating null in the length for safety.
203 ChildLoadedImage->LoadOptions = (VOID *)FullLoadOptions;
204 ChildLoadedImage->LoadOptionsSize = ((UINT32)StrLen(FullLoadOptions) + 1) * sizeof(CHAR16);
205 if (Verbose)
206 Print(L"Using load options '%s'\n", FullLoadOptions);
207 }
208
209 // close open file handles
210 UninitRefitLib();
211
212 // turn control over to the image
213 // TODO: (optionally) re-enable the EFI watchdog timer!
214 ReturnStatus = Status = refit_call3_wrapper(BS->StartImage, ChildImageHandle, NULL, NULL);
215 // control returns here when the child image calls Exit()
216 SPrint(ErrorInfo, 255, L"returned from %s", ImageTitle);
217 if (CheckError(Status, ErrorInfo)) {
218 if (ErrorInStep != NULL)
219 *ErrorInStep = 3;
220 }
221
222 // re-open file handles
223 ReinitRefitLib();
224
225 bailout_unload:
226 // unload the image, we don't care if it works or not...
227 Status = refit_call1_wrapper(BS->UnloadImage, ChildImageHandle);
228 bailout:
229 if (FullLoadOptions != NULL)
230 FreePool(FullLoadOptions);
231 return ReturnStatus;
232 } /* static EFI_STATUS StartEFIImageList() */
233
234 static EFI_STATUS StartEFIImage(IN EFI_DEVICE_PATH *DevicePath,
235 IN CHAR16 *LoadOptions, IN CHAR16 *LoadOptionsPrefix,
236 IN CHAR16 *ImageTitle,
237 OUT UINTN *ErrorInStep,
238 IN BOOLEAN Verbose)
239 {
240 EFI_DEVICE_PATH *DevicePaths[2];
241
242 DevicePaths[0] = DevicePath;
243 DevicePaths[1] = NULL;
244 return StartEFIImageList(DevicePaths, LoadOptions, LoadOptionsPrefix, ImageTitle, ErrorInStep, Verbose);
245 } /* static EFI_STATUS StartEFIImage() */
246
247 //
248 // EFI OS loader functions
249 //
250
251 static VOID StartLoader(IN LOADER_ENTRY *Entry)
252 {
253 UINTN ErrorInStep = 0;
254
255 BeginExternalScreen(Entry->UseGraphicsMode, L"Booting OS");
256 StartEFIImage(Entry->DevicePath, Entry->LoadOptions,
257 Basename(Entry->LoaderPath), Basename(Entry->LoaderPath), &ErrorInStep, !Entry->UseGraphicsMode);
258 FinishExternalScreen();
259 }
260
261 // Locate an initrd or initramfs file that matches the kernel specified by LoaderPath.
262 // The matching file has a name that begins with "init" and includes the same version
263 // number string as is found in LoaderPath -- but not a longer version number string.
264 // For instance, if LoaderPath is \EFI\kernels\bzImage-3.3.0.efi, and if \EFI\kernels
265 // has a file called initramfs-3.3.0.img, this function will return the string
266 // '\EFI\kernels\initramfs-3.3.0.img'. If the directory ALSO contains the file
267 // initramfs-3.3.0-rc7.img or initramfs-13.3.0.img, those files will NOT match;
268 // however, initmine-3.3.0.img might match. (FindInitrd() returns the first match it
269 // finds). Thus, care should be taken to avoid placing duplicate matching files in
270 // the kernel's directory.
271 // If no matching init file can be found, returns NULL.
272 static CHAR16 * FindInitrd(IN CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
273 CHAR16 *InitrdName = NULL, *FileName, *KernelVersion, *InitrdVersion, *Path;
274 REFIT_DIR_ITER DirIter;
275 EFI_FILE_INFO *DirEntry;
276
277 FileName = Basename(LoaderPath);
278 KernelVersion = FindNumbers(FileName);
279 Path = FindPath(LoaderPath);
280
281 // Add trailing backslash for root directory; necessary on some systems, but must
282 // NOT be added to all directories, since on other systems, a trailing backslash on
283 // anything but the root directory causes them to flake out!
284 if (StrLen(Path) == 0) {
285 MergeStrings(&Path, L"\\", 0);
286 } // if
287 DirIterOpen(Volume->RootDir, Path, &DirIter);
288 // Now add a trailing backslash if it was NOT added earlier, for consistency in
289 // building the InitrdName later....
290 if ((StrLen(Path) > 0) && (Path[StrLen(Path) - 1] != L'\\'))
291 MergeStrings(&Path, L"\\", 0);
292 while ((DirIterNext(&DirIter, 2, L"init*", &DirEntry)) && (InitrdName == NULL)) {
293 InitrdVersion = FindNumbers(DirEntry->FileName);
294 if (KernelVersion != NULL) {
295 if (StriCmp(InitrdVersion, KernelVersion) == 0) {
296 MergeStrings(&InitrdName, Path, 0);
297 MergeStrings(&InitrdName, DirEntry->FileName, 0);
298 } // if
299 } else {
300 if (InitrdVersion == NULL) {
301 MergeStrings(&InitrdName, Path, 0);
302 MergeStrings(&InitrdName, DirEntry->FileName, 0);
303 } // if
304 } // if/else
305 if (InitrdVersion != NULL)
306 FreePool(InitrdVersion);
307 } // while
308 DirIterClose(&DirIter);
309
310 // Note: Don't FreePool(FileName), since Basename returns a pointer WITHIN the string it's passed.
311 FreePool(KernelVersion);
312 FreePool(Path);
313 return (InitrdName);
314 } // static CHAR16 * FindInitrd()
315
316 LOADER_ENTRY * AddPreparedLoaderEntry(LOADER_ENTRY *Entry) {
317 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
318
319 return(Entry);
320 } // LOADER_ENTRY * AddPreparedLoaderEntry()
321
322 // Creates a copy of a menu screen.
323 // Returns a pointer to the copy of the menu screen.
324 static REFIT_MENU_SCREEN* CopyMenuScreen(REFIT_MENU_SCREEN *Entry) {
325 REFIT_MENU_SCREEN *NewEntry;
326 UINTN i;
327
328 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
329 if ((Entry != NULL) && (NewEntry != NULL)) {
330 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_SCREEN));
331 NewEntry->Title = StrDuplicate(Entry->Title);
332 NewEntry->TimeoutText = StrDuplicate(Entry->TimeoutText);
333 if (Entry->TitleImage != NULL) {
334 NewEntry->TitleImage = AllocatePool(sizeof(EG_IMAGE));
335 if (NewEntry->TitleImage != NULL)
336 CopyMem(NewEntry->TitleImage, Entry->TitleImage, sizeof(EG_IMAGE));
337 } // if
338 NewEntry->InfoLines = (CHAR16**) AllocateZeroPool(Entry->InfoLineCount * (sizeof(CHAR16*)));
339 for (i = 0; i < Entry->InfoLineCount && NewEntry->InfoLines; i++) {
340 NewEntry->InfoLines[i] = StrDuplicate(Entry->InfoLines[i]);
341 } // for
342 NewEntry->Entries = (REFIT_MENU_ENTRY**) AllocateZeroPool(Entry->EntryCount * (sizeof (REFIT_MENU_ENTRY*)));
343 for (i = 0; i < Entry->EntryCount && NewEntry->Entries; i++) {
344 AddMenuEntry(NewEntry, Entry->Entries[i]);
345 } // for
346 } // if
347 return (NewEntry);
348 } // static REFIT_MENU_SCREEN* CopyMenuScreen()
349
350 // Creates a copy of a menu entry. Intended to enable moving a stack-based
351 // menu entry (such as the ones for the "reboot" and "exit" functions) to
352 // to the heap. This enables easier deletion of the whole set of menu
353 // entries when re-scanning.
354 // Returns a pointer to the copy of the menu entry.
355 static REFIT_MENU_ENTRY* CopyMenuEntry(REFIT_MENU_ENTRY *Entry) {
356 REFIT_MENU_ENTRY *NewEntry;
357
358 NewEntry = AllocateZeroPool(sizeof(REFIT_MENU_ENTRY));
359 if ((Entry != NULL) && (NewEntry != NULL)) {
360 CopyMem(NewEntry, Entry, sizeof(REFIT_MENU_ENTRY));
361 NewEntry->Title = StrDuplicate(Entry->Title);
362 if (Entry->BadgeImage != NULL) {
363 NewEntry->BadgeImage = AllocatePool(sizeof(EG_IMAGE));
364 if (NewEntry->BadgeImage != NULL)
365 CopyMem(NewEntry->BadgeImage, Entry->BadgeImage, sizeof(EG_IMAGE));
366 }
367 if (Entry->Image != NULL) {
368 NewEntry->Image = AllocatePool(sizeof(EG_IMAGE));
369 if (NewEntry->Image != NULL)
370 CopyMem(NewEntry->Image, Entry->Image, sizeof(EG_IMAGE));
371 }
372 if (Entry->SubScreen != NULL) {
373 NewEntry->SubScreen = CopyMenuScreen(Entry->SubScreen);
374 }
375 } // if
376 return (NewEntry);
377 } // REFIT_MENU_ENTRY* CopyMenuEntry()
378
379 // Creates a new LOADER_ENTRY data structure and populates it with
380 // default values from the specified Entry, or NULL values if Entry
381 // is unspecified (NULL).
382 // Returns a pointer to the new data structure, or NULL if it
383 // couldn't be allocated
384 LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) {
385 LOADER_ENTRY *NewEntry = NULL;
386
387 NewEntry = AllocateZeroPool(sizeof(LOADER_ENTRY));
388 if (NewEntry != NULL) {
389 NewEntry->me.Title = NULL;
390 NewEntry->me.Tag = TAG_LOADER;
391 NewEntry->Enabled = TRUE;
392 NewEntry->UseGraphicsMode = FALSE;
393 NewEntry->OSType = 0;
394 if (Entry != NULL) {
395 NewEntry->LoaderPath = StrDuplicate(Entry->LoaderPath);
396 NewEntry->VolName = StrDuplicate(Entry->VolName);
397 NewEntry->DevicePath = Entry->DevicePath;
398 NewEntry->UseGraphicsMode = Entry->UseGraphicsMode;
399 NewEntry->LoadOptions = StrDuplicate(Entry->LoadOptions);
400 NewEntry->InitrdPath = StrDuplicate(Entry->InitrdPath);
401 }
402 } // if
403 return (NewEntry);
404 } // LOADER_ENTRY *InitializeLoaderEntry()
405
406 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
407 // the default entry that launches the boot loader using the same options as the
408 // main Entry does. Subsequent options can be added by the calling function.
409 // If a subscreen already exists in the Entry that's passed to this function,
410 // it's left unchanged and a pointer to it is returned.
411 // Returns a pointer to the new subscreen data structure, or NULL if there
412 // were problems allocating memory.
413 REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) {
414 CHAR16 *FileName, *Temp = NULL, *TitleStr;
415 REFIT_MENU_SCREEN *SubScreen = NULL;
416 LOADER_ENTRY *SubEntry;
417
418 FileName = Basename(Entry->LoaderPath);
419 if (Entry->me.SubScreen == NULL) { // No subscreen yet; initialize default entry....
420 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
421 if (SubScreen != NULL) {
422 TitleStr = AllocateZeroPool(sizeof(CHAR16) * 256);
423 SPrint(TitleStr, 255, L"Boot Options for %s on %s", (Entry->Title != NULL) ? Entry->Title : FileName, Entry->VolName);
424 SubScreen->Title = TitleStr;
425 SubScreen->TitleImage = Entry->me.Image;
426 // default entry
427 SubEntry = InitializeLoaderEntry(Entry);
428 if (SubEntry != NULL) {
429 SubEntry->me.Title = L"Boot using default options";
430 if ((SubEntry->InitrdPath != NULL) && (StrLen(SubEntry->InitrdPath) > 0) && (!StriSubCmp(L"initrd", SubEntry->LoadOptions))) {
431 MergeStrings(&Temp, L"initrd=", 0);
432 MergeStrings(&Temp, SubEntry->InitrdPath, 0);
433 MergeStrings(&SubEntry->LoadOptions, Temp, L' ');
434 FreePool(Temp);
435 } // if
436 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
437 } // if (SubEntry != NULL)
438 } // if (SubScreen != NULL)
439 } else { // existing subscreen; less initialization, and just add new entry later....
440 SubScreen = Entry->me.SubScreen;
441 } // if/else
442 return SubScreen;
443 } // REFIT_MENU_SCREEN *InitializeSubScreen()
444
445 VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
446 REFIT_MENU_SCREEN *SubScreen;
447 LOADER_ENTRY *SubEntry;
448 CHAR16 *InitrdOption = NULL, *Temp;
449 CHAR16 DiagsFileName[256];
450 REFIT_FILE *File;
451 UINTN TokenCount;
452 CHAR16 **TokenList;
453
454 // create the submenu
455 if (StrLen(Entry->Title) == 0) {
456 FreePool(Entry->Title);
457 Entry->Title = NULL;
458 }
459 SubScreen = InitializeSubScreen(Entry);
460
461 // loader-specific submenu entries
462 if (Entry->OSType == 'M') { // entries for Mac OS X
463 #if defined(EFIX64)
464 SubEntry = InitializeLoaderEntry(Entry);
465 if (SubEntry != NULL) {
466 SubEntry->me.Title = L"Boot Mac OS X with a 64-bit kernel";
467 SubEntry->LoadOptions = L"arch=x86_64";
468 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
469 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
470 } // if
471
472 SubEntry = InitializeLoaderEntry(Entry);
473 if (SubEntry != NULL) {
474 SubEntry->me.Title = L"Boot Mac OS X with a 32-bit kernel";
475 SubEntry->LoadOptions = L"arch=i386";
476 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
477 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
478 } // if
479 #endif
480
481 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SINGLEUSER)) {
482 SubEntry = InitializeLoaderEntry(Entry);
483 if (SubEntry != NULL) {
484 SubEntry->me.Title = L"Boot Mac OS X in verbose mode";
485 SubEntry->UseGraphicsMode = FALSE;
486 SubEntry->LoadOptions = L"-v";
487 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
488 } // if
489
490 #if defined(EFIX64)
491 SubEntry = InitializeLoaderEntry(Entry);
492 if (SubEntry != NULL) {
493 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (64-bit)";
494 SubEntry->UseGraphicsMode = FALSE;
495 SubEntry->LoadOptions = L"-v arch=x86_64";
496 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
497 }
498
499 SubEntry = InitializeLoaderEntry(Entry);
500 if (SubEntry != NULL) {
501 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (32-bit)";
502 SubEntry->UseGraphicsMode = FALSE;
503 SubEntry->LoadOptions = L"-v arch=i386";
504 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
505 }
506 #endif
507
508 SubEntry = InitializeLoaderEntry(Entry);
509 if (SubEntry != NULL) {
510 SubEntry->me.Title = L"Boot Mac OS X in single user mode";
511 SubEntry->UseGraphicsMode = FALSE;
512 SubEntry->LoadOptions = L"-v -s";
513 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
514 } // if
515 } // not single-user
516
517 // check for Apple hardware diagnostics
518 StrCpy(DiagsFileName, L"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
519 if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) {
520 SubEntry = InitializeLoaderEntry(Entry);
521 if (SubEntry != NULL) {
522 SubEntry->me.Title = L"Run Apple Hardware Test";
523 FreePool(SubEntry->LoaderPath);
524 SubEntry->LoaderPath = StrDuplicate(DiagsFileName);
525 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
526 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
527 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
528 } // if
529 } // if diagnostics entry found
530
531 } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders
532 File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume);
533 if (File != NULL) {
534 if ((Temp = FindInitrd(Entry->LoaderPath, Volume)) != NULL) {
535 MergeStrings(&InitrdOption, L"initrd=", 0);
536 MergeStrings(&InitrdOption, Temp, 0);
537 }
538 TokenCount = ReadTokenLine(File, &TokenList); // read and discard first entry, since it's
539 FreeTokenLine(&TokenList, &TokenCount); // set up by InitializeSubScreen(), earlier....
540 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
541 SubEntry = InitializeLoaderEntry(Entry);
542 SubEntry->me.Title = StrDuplicate(TokenList[0]);
543 if (SubEntry->LoadOptions != NULL)
544 FreePool(SubEntry->LoadOptions);
545 SubEntry->LoadOptions = StrDuplicate(TokenList[1]);
546 MergeStrings(&SubEntry->LoadOptions, InitrdOption, L' ');
547 FreeTokenLine(&TokenList, &TokenCount);
548 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
549 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
550 } // while
551 if (InitrdOption)
552 FreePool(InitrdOption);
553 if (Temp)
554 FreePool(Temp);
555 FreePool(File);
556 } // if Linux options file exists
557
558 } else if (Entry->OSType == 'E') { // entries for ELILO
559 SubEntry = InitializeLoaderEntry(Entry);
560 if (SubEntry != NULL) {
561 SubEntry->me.Title = L"Run ELILO in interactive mode";
562 SubEntry->LoadOptions = L"-p";
563 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
564 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
565 }
566
567 SubEntry = InitializeLoaderEntry(Entry);
568 if (SubEntry != NULL) {
569 SubEntry->me.Title = L"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
570 SubEntry->UseGraphicsMode = TRUE;
571 SubEntry->LoadOptions = L"-d 0 i17";
572 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
573 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
574 }
575
576 SubEntry = InitializeLoaderEntry(Entry);
577 if (SubEntry != NULL) {
578 SubEntry->me.Title = L"Boot Linux for a 20\" iMac (*)";
579 SubEntry->UseGraphicsMode = TRUE;
580 SubEntry->LoadOptions = L"-d 0 i20";
581 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
582 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
583 }
584
585 SubEntry = InitializeLoaderEntry(Entry);
586 if (SubEntry != NULL) {
587 SubEntry->me.Title = L"Boot Linux for a Mac Mini (*)";
588 SubEntry->UseGraphicsMode = TRUE;
589 SubEntry->LoadOptions = L"-d 0 mini";
590 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
591 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
592 }
593
594 AddMenuInfoLine(SubScreen, L"NOTE: This is an example. Entries");
595 AddMenuInfoLine(SubScreen, L"marked with (*) may not work.");
596
597 } else if (Entry->OSType == 'X') { // entries for xom.efi
598 // by default, skip the built-in selection and boot from hard disk only
599 Entry->LoadOptions = L"-s -h";
600
601 SubEntry = InitializeLoaderEntry(Entry);
602 if (SubEntry != NULL) {
603 SubEntry->me.Title = L"Boot Windows from Hard Disk";
604 SubEntry->LoadOptions = L"-s -h";
605 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
606 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
607 }
608
609 SubEntry = InitializeLoaderEntry(Entry);
610 if (SubEntry != NULL) {
611 SubEntry->me.Title = L"Boot Windows from CD-ROM";
612 SubEntry->LoadOptions = L"-s -c";
613 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
614 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
615 }
616
617 SubEntry = InitializeLoaderEntry(Entry);
618 if (SubEntry != NULL) {
619 SubEntry->me.Title = L"Run XOM in text mode";
620 SubEntry->UseGraphicsMode = FALSE;
621 SubEntry->LoadOptions = L"-v";
622 SubEntry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
623 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
624 }
625 } // entries for xom.efi
626 AddMenuEntry(SubScreen, &MenuEntryReturn);
627 Entry->me.SubScreen = SubScreen;
628 } // VOID GenerateSubScreen()
629
630 // Returns options for a Linux kernel. Reads them from an options file in the
631 // kernel's directory; and if present, adds an initrd= option for an initial
632 // RAM disk file with the same version number as the kernel file.
633 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
634 CHAR16 *Options = NULL, *InitrdName, *InitrdOption = NULL;
635
636 Options = GetFirstOptionsFromFile(LoaderPath, Volume);
637 InitrdName = FindInitrd(LoaderPath, Volume);
638 if (InitrdName != NULL) {
639 MergeStrings(&InitrdOption, L"initrd=", 0);
640 MergeStrings(&InitrdOption, InitrdName, 0);
641 } // if
642 MergeStrings(&Options, InitrdOption, ' ');
643 if (InitrdOption != NULL)
644 FreePool(InitrdOption);
645 if (InitrdName != NULL)
646 FreePool(InitrdName);
647 return (Options);
648 } // static CHAR16 * GetMainLinuxOptions()
649
650 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
651 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
652 // that will (with luck) work fairly automatically.
653 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
654 CHAR16 IconFileName[256];
655 CHAR16 *FileName, *PathOnly, *OSIconName = NULL, *Temp;
656 CHAR16 ShortcutLetter = 0;
657
658 FileName = Basename(LoaderPath);
659 PathOnly = FindPath(LoaderPath);
660
661 // locate a custom icon for the loader
662 StrCpy(IconFileName, LoaderPath);
663 ReplaceEfiExtension(IconFileName, L".icns");
664 if (FileExists(Volume->RootDir, IconFileName)) {
665 Entry->me.Image = LoadIcns(Volume->RootDir, IconFileName, 128);
666 } else if ((StrLen(PathOnly) == 0) && (Volume->VolIconImage != NULL)) {
667 Entry->me.Image = Volume->VolIconImage;
668 } // icon matched to loader or volume
669
670 Temp = FindLastDirName(LoaderPath);
671 MergeStrings(&OSIconName, Temp, L',');
672 FreePool(Temp);
673 if (OSIconName != NULL) {
674 ShortcutLetter = OSIconName[0];
675 }
676
677 // detect specific loaders
678 if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) {
679 MergeStrings(&OSIconName, L"linux", L',');
680 Entry->OSType = 'L';
681 if (ShortcutLetter == 0)
682 ShortcutLetter = 'L';
683 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
684 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_LINUX;
685 } else if (StriSubCmp(L"refit", LoaderPath)) {
686 MergeStrings(&OSIconName, L"refit", L',');
687 Entry->OSType = 'R';
688 ShortcutLetter = 'R';
689 } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) {
690 if (Volume->VolIconImage != NULL) { // custom icon file found
691 Entry->me.Image = Volume->VolIconImage;
692 }
693 MergeStrings(&OSIconName, L"mac", L',');
694 Entry->OSType = 'M';
695 ShortcutLetter = 'M';
696 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_OSX;
697 } else if (StriCmp(FileName, L"diags.efi") == 0) {
698 MergeStrings(&OSIconName, L"hwtest", L',');
699 } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0) {
700 MergeStrings(&OSIconName, L"elilo,linux", L',');
701 Entry->OSType = 'E';
702 if (ShortcutLetter == 0)
703 ShortcutLetter = 'L';
704 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_ELILO;
705 } else if (StriSubCmp(L"grub", FileName)) {
706 Entry->OSType = 'G';
707 ShortcutLetter = 'G';
708 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_GRUB;
709 } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
710 StriCmp(FileName, L"bootmgr.efi") == 0 ||
711 StriCmp(FileName, L"Bootmgfw.efi") == 0) {
712 MergeStrings(&OSIconName, L"win", L',');
713 Entry->OSType = 'W';
714 ShortcutLetter = 'W';
715 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
716 } else if (StriCmp(FileName, L"xom.efi") == 0) {
717 MergeStrings(&OSIconName, L"xom,win", L',');
718 Entry->UseGraphicsMode = TRUE;
719 Entry->OSType = 'X';
720 ShortcutLetter = 'W';
721 Entry->UseGraphicsMode = GlobalConfig.GraphicsFor & GRAPHICS_FOR_WINDOWS;
722 }
723
724 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
725 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
726 Entry->me.ShortcutLetter = ShortcutLetter;
727 if (Entry->me.Image == NULL)
728 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
729 if (PathOnly != NULL)
730 FreePool(PathOnly);
731 } // VOID SetLoaderDefaults()
732
733 // Add a specified EFI boot loader to the list, using automatic settings
734 // for icons, options, etc.
735 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
736 LOADER_ENTRY *Entry;
737 CHAR16 *PoolStr;
738
739 CleanUpPathNameSlashes(LoaderPath);
740 Entry = InitializeLoaderEntry(NULL);
741 if (Entry != NULL) {
742 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
743 PoolStr = AllocateZeroPool(sizeof(CHAR16) * 256);
744 SPrint(PoolStr, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
745 Entry->me.Title = PoolStr;
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 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1146
1147 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep, TRUE);
1148 if (Status == EFI_NOT_FOUND) {
1149 if (ErrorInStep == 1) {
1150 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1151 } else if (ErrorInStep == 3) {
1152 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1153 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1154 }
1155 }
1156 FinishExternalScreen();
1157 } /* static VOID StartLegacy() */
1158
1159 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1160 {
1161 LEGACY_ENTRY *Entry, *SubEntry;
1162 REFIT_MENU_SCREEN *SubScreen;
1163 CHAR16 *VolDesc;
1164 CHAR16 ShortcutLetter = 0;
1165 CHAR16 *PoolStr;
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 PoolStr = AllocateZeroPool(256 * sizeof(CHAR16));
1183 SPrint(PoolStr, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1184 Entry->me.Title = PoolStr;
1185 Entry->me.Tag = TAG_LEGACY;
1186 Entry->me.Row = 0;
1187 Entry->me.ShortcutLetter = ShortcutLetter;
1188 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1189 Entry->me.BadgeImage = Volume->VolBadgeImage;
1190 Entry->Volume = Volume;
1191 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1192 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1193 Entry->Enabled = TRUE;
1194
1195 // create the submenu
1196 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1197 PoolStr = AllocateZeroPool(256 * sizeof(CHAR16));
1198 SPrint(PoolStr, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1199 SubScreen->Title = PoolStr;
1200 SubScreen->TitleImage = Entry->me.Image;
1201
1202 // default entry
1203 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1204 PoolStr = AllocateZeroPool(256 * sizeof(CHAR16));
1205 SPrint(PoolStr, 255, L"Boot %s", LoaderTitle);
1206 SubEntry->me.Title = PoolStr;
1207 SubEntry->me.Tag = TAG_LEGACY;
1208 SubEntry->Volume = Entry->Volume;
1209 SubEntry->LoadOptions = Entry->LoadOptions;
1210 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1211
1212 AddMenuEntry(SubScreen, &MenuEntryReturn);
1213 Entry->me.SubScreen = SubScreen;
1214 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1215 return Entry;
1216 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1217
1218 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1219 UINTN VolumeIndex2;
1220 BOOLEAN ShowVolume, HideIfOthersFound;
1221
1222 ShowVolume = FALSE;
1223 HideIfOthersFound = FALSE;
1224 if (Volume->IsAppleLegacy) {
1225 ShowVolume = TRUE;
1226 HideIfOthersFound = TRUE;
1227 } else if (Volume->HasBootCode) {
1228 ShowVolume = TRUE;
1229 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1230 Volume->BlockIOOffset == 0 &&
1231 Volume->OSName == NULL)
1232 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1233 HideIfOthersFound = TRUE;
1234 }
1235 if (HideIfOthersFound) {
1236 // check for other bootable entries on the same disk
1237 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1238 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1239 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1240 ShowVolume = FALSE;
1241 }
1242 }
1243
1244 if (ShowVolume)
1245 AddLegacyEntry(NULL, Volume);
1246 } // static VOID ScanLegacyVolume()
1247
1248 // Scan attached optical discs for legacy (BIOS) boot code
1249 // and add anything found to the list....
1250 static VOID ScanLegacyDisc(VOID)
1251 {
1252 UINTN VolumeIndex;
1253 REFIT_VOLUME *Volume;
1254
1255 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1256 Volume = Volumes[VolumeIndex];
1257 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1258 ScanLegacyVolume(Volume, VolumeIndex);
1259 } // for
1260 } /* static VOID ScanLegacyDisc() */
1261
1262 // Scan internal hard disks for legacy (BIOS) boot code
1263 // and add anything found to the list....
1264 static VOID ScanLegacyInternal(VOID)
1265 {
1266 UINTN VolumeIndex;
1267 REFIT_VOLUME *Volume;
1268
1269 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1270 Volume = Volumes[VolumeIndex];
1271 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1272 ScanLegacyVolume(Volume, VolumeIndex);
1273 } // for
1274 } /* static VOID ScanLegacyInternal() */
1275
1276 // Scan external disks for legacy (BIOS) boot code
1277 // and add anything found to the list....
1278 static VOID ScanLegacyExternal(VOID)
1279 {
1280 UINTN VolumeIndex;
1281 REFIT_VOLUME *Volume;
1282
1283 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1284 Volume = Volumes[VolumeIndex];
1285 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1286 ScanLegacyVolume(Volume, VolumeIndex);
1287 } // for
1288 } /* static VOID ScanLegacyExternal() */
1289
1290 //
1291 // pre-boot tool functions
1292 //
1293
1294 static VOID StartTool(IN LOADER_ENTRY *Entry)
1295 {
1296 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1297 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1298 Basename(Entry->LoaderPath), NULL, TRUE);
1299 FinishExternalScreen();
1300 } /* static VOID StartTool() */
1301
1302 static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1303 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1304 {
1305 LOADER_ENTRY *Entry;
1306 CHAR16 *TitleStr = NULL;
1307
1308 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1309
1310 MergeStrings(&TitleStr, L"Start ", 0);
1311 MergeStrings(&TitleStr, LoaderTitle, 0);
1312 Entry->me.Title = TitleStr;
1313 Entry->me.Tag = TAG_TOOL;
1314 Entry->me.Row = 1;
1315 Entry->me.ShortcutLetter = ShortcutLetter;
1316 Entry->me.Image = Image;
1317 Entry->LoaderPath = StrDuplicate(LoaderPath);
1318 Entry->DevicePath = FileDevicePath(SelfLoadedImage->DeviceHandle, Entry->LoaderPath);
1319 Entry->UseGraphicsMode = UseGraphicsMode;
1320
1321 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1322 return Entry;
1323 } /* static LOADER_ENTRY * AddToolEntry() */
1324
1325 //
1326 // pre-boot driver functions
1327 //
1328
1329 static UINTN ScanDriverDir(IN CHAR16 *Path)
1330 {
1331 EFI_STATUS Status;
1332 REFIT_DIR_ITER DirIter;
1333 UINTN NumFound = 0;
1334 EFI_FILE_INFO *DirEntry;
1335 CHAR16 FileName[256];
1336
1337 CleanUpPathNameSlashes(Path);
1338 // look through contents of the directory
1339 DirIterOpen(SelfRootDir, Path, &DirIter);
1340 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1341 if (DirEntry->FileName[0] == '.')
1342 continue; // skip this
1343
1344 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1345 NumFound++;
1346 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1347 L"", DirEntry->FileName, DirEntry->FileName, NULL, FALSE);
1348 }
1349 Status = DirIterClose(&DirIter);
1350 if (Status != EFI_NOT_FOUND) {
1351 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1352 CheckError(Status, FileName);
1353 }
1354 return (NumFound);
1355 }
1356
1357 #ifdef __MAKEWITH_GNUEFI
1358 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1359 {
1360 EFI_STATUS Status;
1361 UINTN AllHandleCount;
1362 EFI_HANDLE *AllHandleBuffer;
1363 UINTN Index;
1364 UINTN HandleCount;
1365 EFI_HANDLE *HandleBuffer;
1366 UINT32 *HandleType;
1367 UINTN HandleIndex;
1368 BOOLEAN Parent;
1369 BOOLEAN Device;
1370
1371 Status = LibLocateHandle(AllHandles,
1372 NULL,
1373 NULL,
1374 &AllHandleCount,
1375 &AllHandleBuffer);
1376 if (EFI_ERROR(Status))
1377 return Status;
1378
1379 for (Index = 0; Index < AllHandleCount; Index++) {
1380 //
1381 // Scan the handle database
1382 //
1383 Status = LibScanHandleDatabase(NULL,
1384 NULL,
1385 AllHandleBuffer[Index],
1386 NULL,
1387 &HandleCount,
1388 &HandleBuffer,
1389 &HandleType);
1390 if (EFI_ERROR (Status))
1391 goto Done;
1392
1393 Device = TRUE;
1394 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1395 Device = FALSE;
1396 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1397 Device = FALSE;
1398
1399 if (Device) {
1400 Parent = FALSE;
1401 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1402 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1403 Parent = TRUE;
1404 } // for
1405
1406 if (!Parent) {
1407 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1408 Status = refit_call4_wrapper(BS->ConnectController,
1409 AllHandleBuffer[Index],
1410 NULL,
1411 NULL,
1412 TRUE);
1413 }
1414 }
1415 }
1416
1417 FreePool (HandleBuffer);
1418 FreePool (HandleType);
1419 }
1420
1421 Done:
1422 FreePool (AllHandleBuffer);
1423 return Status;
1424 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1425 #else
1426 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1427 BdsLibConnectAllDriversToAllControllers();
1428 return 0;
1429 }
1430 #endif
1431
1432 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1433 // directories specified by the user in the "scan_driver_dirs" configuration
1434 // file line.
1435 static VOID LoadDrivers(VOID)
1436 {
1437 CHAR16 *Directory, *SelfDirectory;
1438 UINTN i = 0, Length, NumFound = 0;
1439
1440 // load drivers from the subdirectories of rEFInd's home directory specified
1441 // in the DRIVER_DIRS constant.
1442 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1443 SelfDirectory = StrDuplicate(SelfDirPath);
1444 CleanUpPathNameSlashes(SelfDirectory);
1445 MergeStrings(&SelfDirectory, Directory, L'\\');
1446 NumFound += ScanDriverDir(SelfDirectory);
1447 FreePool(Directory);
1448 FreePool(SelfDirectory);
1449 }
1450
1451 // Scan additional user-specified driver directories....
1452 i = 0;
1453 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1454 CleanUpPathNameSlashes(Directory);
1455 Length = StrLen(Directory);
1456 if (Length > 0)
1457 NumFound += ScanDriverDir(Directory);
1458 FreePool(Directory);
1459 } // while
1460
1461 // connect all devices
1462 if (NumFound > 0)
1463 ConnectAllDriversToAllControllers();
1464 } /* static VOID LoadDrivers() */
1465
1466 static VOID ScanForBootloaders(VOID) {
1467 UINTN i;
1468
1469 ScanVolumes();
1470
1471 // scan for loaders and tools, add them to the menu
1472 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1473 switch(GlobalConfig.ScanFor[i]) {
1474 case 'c': case 'C':
1475 ScanLegacyDisc();
1476 break;
1477 case 'h': case 'H':
1478 ScanLegacyInternal();
1479 break;
1480 case 'b': case 'B':
1481 ScanLegacyExternal();
1482 break;
1483 case 'm': case 'M':
1484 ScanUserConfigured();
1485 break;
1486 case 'e': case 'E':
1487 ScanExternal();
1488 break;
1489 case 'i': case 'I':
1490 ScanInternal();
1491 break;
1492 case 'o': case 'O':
1493 ScanOptical();
1494 break;
1495 } // switch()
1496 } // for
1497
1498 // assign shortcut keys
1499 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1500 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1501
1502 // wait for user ACK when there were errors
1503 FinishTextScreen(FALSE);
1504 } // static VOID ScanForBootloaders()
1505
1506 // Add the second-row tags containing built-in and external tools (EFI shell,
1507 // reboot, etc.)
1508 static VOID ScanForTools(VOID) {
1509 CHAR16 *FileName = NULL;
1510 REFIT_MENU_ENTRY *TempMenuEntry;
1511 UINTN i, j;
1512
1513 for (i = 0; i < NUM_TOOLS; i++) {
1514 switch(GlobalConfig.ShowTools[i]) {
1515 case TAG_SHUTDOWN:
1516 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1517 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1518 AddMenuEntry(&MainMenu, TempMenuEntry);
1519 break;
1520 case TAG_REBOOT:
1521 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1522 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1523 AddMenuEntry(&MainMenu, TempMenuEntry);
1524 break;
1525 case TAG_ABOUT:
1526 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1527 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1528 AddMenuEntry(&MainMenu, TempMenuEntry);
1529 break;
1530 case TAG_EXIT:
1531 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1532 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1533 AddMenuEntry(&MainMenu, TempMenuEntry);
1534 break;
1535 case TAG_SHELL:
1536 j = 0;
1537 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1538 if (FileExists(SelfRootDir, FileName)) {
1539 AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE);
1540 }
1541 } // while
1542 break;
1543 case TAG_GPTSYNC:
1544 MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1545 if (FileExists(SelfRootDir, FileName)) {
1546 AddToolEntry(FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1547 }
1548 break;
1549 } // switch()
1550 if (FileName != NULL) {
1551 FreePool(FileName);
1552 FileName = NULL;
1553 }
1554 } // for
1555 } // static VOID ScanForTools
1556
1557 // Rescan for boot loaders
1558 VOID RescanAll(VOID) {
1559 EG_PIXEL BGColor;
1560
1561 BGColor.b = 255;
1562 BGColor.g = 175;
1563 BGColor.r = 100;
1564 BGColor.a = 0;
1565 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
1566 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
1567 MainMenu.Entries = NULL;
1568 MainMenu.EntryCount = 0;
1569 ReadConfig();
1570 ConnectAllDriversToAllControllers();
1571 ScanForBootloaders();
1572 ScanForTools();
1573 SetupScreen();
1574 } // VOID RescanAll()
1575
1576 #ifndef __MAKEWITH_GNUEFI
1577
1578 // Minimal initialization function
1579 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
1580 gST = SystemTable;
1581 // gImageHandle = ImageHandle;
1582 gBS = SystemTable->BootServices;
1583 // gRS = SystemTable->RuntimeServices;
1584 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
1585
1586 InitializeConsoleSim();
1587 }
1588
1589 #endif
1590
1591 //
1592 // main entry point
1593 //
1594 EFI_STATUS
1595 EFIAPI
1596 efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
1597 {
1598 EFI_STATUS Status;
1599 BOOLEAN MainLoopRunning = TRUE;
1600 REFIT_MENU_ENTRY *ChosenEntry;
1601 UINTN MenuExit;
1602 CHAR16 *Selection;
1603
1604 // bootstrap
1605 InitializeLib(ImageHandle, SystemTable);
1606 InitScreen();
1607 Status = InitRefitLib(ImageHandle);
1608 if (EFI_ERROR(Status))
1609 return Status;
1610
1611 // read configuration
1612 CopyMem(GlobalConfig.ScanFor, "ieo ", NUM_SCAN_OPTIONS);
1613 ReadConfig();
1614 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
1615
1616 // disable EFI watchdog timer
1617 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
1618
1619 // further bootstrap (now with config available)
1620 SetupScreen();
1621 LoadDrivers();
1622 ScanForBootloaders();
1623 ScanForTools();
1624
1625 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
1626 while (MainLoopRunning) {
1627 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
1628
1629 // The Escape key triggers a re-scan operation....
1630 if (MenuExit == MENU_EXIT_ESCAPE) {
1631 RescanAll();
1632 continue;
1633 }
1634
1635 switch (ChosenEntry->Tag) {
1636
1637 case TAG_REBOOT: // Reboot
1638 TerminateScreen();
1639 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1640 MainLoopRunning = FALSE; // just in case we get this far
1641 break;
1642
1643 case TAG_SHUTDOWN: // Shut Down
1644 TerminateScreen();
1645 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1646 MainLoopRunning = FALSE; // just in case we get this far
1647 break;
1648
1649 case TAG_ABOUT: // About rEFInd
1650 AboutrEFInd();
1651 break;
1652
1653 case TAG_LOADER: // Boot OS via .EFI loader
1654 StartLoader((LOADER_ENTRY *)ChosenEntry);
1655 break;
1656
1657 case TAG_LEGACY: // Boot legacy OS
1658 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
1659 break;
1660
1661 case TAG_TOOL: // Start a EFI tool
1662 StartTool((LOADER_ENTRY *)ChosenEntry);
1663 break;
1664
1665 case TAG_EXIT: // Terminate rEFInd
1666 BeginTextScreen(L" ");
1667 return EFI_SUCCESS;
1668 break;
1669
1670 } // switch()
1671 FreePool(Selection);
1672 Selection = StrDuplicate(ChosenEntry->Title);
1673 } // while()
1674
1675 // If we end up here, things have gone wrong. Try to reboot, and if that
1676 // fails, go into an endless loop.
1677 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1678 EndlessIdleLoop();
1679
1680 return EFI_SUCCESS;
1681 } /* efi_main() */