]> code.delx.au - refind/blob - refind/main.c
Fixed compilation bug for 32-bit systems that caused filesystem name issues.
[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.3.2");
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 }
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;
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 SubScreen->Title = AllocateZeroPool(sizeof(CHAR16) * 256);
423 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s",
424 (Entry->Title != NULL) ? Entry->Title : FileName, Entry->VolName);
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
738 CleanUpPathNameSlashes(LoaderPath);
739 Entry = InitializeLoaderEntry(NULL);
740 if (Entry != NULL) {
741 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
742 Entry->me.Title = AllocateZeroPool(sizeof(CHAR16) * 256);
743 SPrint(Entry->me.Title, 255, L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
744 Entry->me.Row = 0;
745 Entry->me.BadgeImage = Volume->VolBadgeImage;
746 if ((LoaderPath != NULL) && (LoaderPath[0] != L'\\')) {
747 Entry->LoaderPath = StrDuplicate(L"\\");
748 } else {
749 Entry->LoaderPath = NULL;
750 }
751 MergeStrings(&(Entry->LoaderPath), LoaderPath, 0);
752 Entry->VolName = Volume->VolName;
753 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
754 SetLoaderDefaults(Entry, LoaderPath, Volume);
755 GenerateSubScreen(Entry, Volume);
756 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
757 }
758
759 return(Entry);
760 } // LOADER_ENTRY * AddLoaderEntry()
761
762 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
763 // (Time1 == Time2). Precision is only to the nearest second; since
764 // this is used for sorting boot loader entries, differences smaller
765 // than this are likely to be meaningless (and unlikely!).
766 INTN TimeComp(EFI_TIME *Time1, EFI_TIME *Time2) {
767 INT64 Time1InSeconds, Time2InSeconds;
768
769 // Following values are overestimates; I'm assuming 31 days in every month.
770 // This is fine for the purpose of this function, which has a limited
771 // purpose.
772 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
773 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
774 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
775 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
776 if (Time1InSeconds < Time2InSeconds)
777 return (-1);
778 else if (Time1InSeconds > Time2InSeconds)
779 return (1);
780
781 return 0;
782 } // INTN TimeComp()
783
784 // Adds a loader list element, keeping it sorted by date. Returns the new
785 // first element (the one with the most recent date).
786 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
787 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
788
789 LatestEntry = CurrentEntry = LoaderList;
790 if (LoaderList == NULL) {
791 LatestEntry = NewEntry;
792 } else {
793 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
794 PrevEntry = CurrentEntry;
795 CurrentEntry = CurrentEntry->NextEntry;
796 } // while
797 NewEntry->NextEntry = CurrentEntry;
798 if (PrevEntry == NULL) {
799 LatestEntry = NewEntry;
800 } else {
801 PrevEntry->NextEntry = NewEntry;
802 } // if/else
803 } // if/else
804 return (LatestEntry);
805 } // static VOID AddLoaderListEntry()
806
807 // Delete the LOADER_LIST linked list
808 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
809 struct LOADER_LIST *Temp;
810
811 while (LoaderList != NULL) {
812 Temp = LoaderList;
813 LoaderList = LoaderList->NextEntry;
814 FreePool(Temp->FileName);
815 FreePool(Temp);
816 } // while
817 } // static VOID CleanUpLoaderList()
818
819 // Scan an individual directory for EFI boot loader files and, if found,
820 // add them to the list. Sorts the entries within the loader directory
821 // so that the most recent one appears first in the list.
822 static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
823 {
824 EFI_STATUS Status;
825 REFIT_DIR_ITER DirIter;
826 EFI_FILE_INFO *DirEntry;
827 CHAR16 FileName[256], *Extension;
828 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
829
830 if ((!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && Volume->DeviceHandle != SelfVolume->DeviceHandle) ||
831 (StriCmp(Path, SelfDirPath) != 0)) && (!IsIn(Path, GlobalConfig.DontScan))) {
832 // look through contents of the directory
833 DirIterOpen(Volume->RootDir, Path, &DirIter);
834 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
835 Extension = FindExtension(DirEntry->FileName);
836 if (DirEntry->FileName[0] == '.' ||
837 StriCmp(DirEntry->FileName, L"TextMode.efi") == 0 ||
838 StriCmp(DirEntry->FileName, L"ebounce.efi") == 0 ||
839 StriCmp(DirEntry->FileName, L"GraphicsConsole.efi") == 0 ||
840 StriCmp(Extension, L".icns") == 0 ||
841 StriSubCmp(L"shell", DirEntry->FileName))
842 continue; // skip this
843
844 if (Path)
845 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
846 else
847 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
848 CleanUpPathNameSlashes(FileName);
849 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
850 if (NewLoader != NULL) {
851 NewLoader->FileName = StrDuplicate(FileName);
852 NewLoader->TimeStamp = DirEntry->ModificationTime;
853 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
854 } // if
855 FreePool(Extension);
856 } // while
857 NewLoader = LoaderList;
858 while (NewLoader != NULL) {
859 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
860 NewLoader = NewLoader->NextEntry;
861 } // while
862 CleanUpLoaderList(LoaderList);
863 Status = DirIterClose(&DirIter);
864 if (Status != EFI_NOT_FOUND) {
865 if (Path)
866 SPrint(FileName, 255, L"while scanning the %s directory", Path);
867 else
868 StrCpy(FileName, L"while scanning the root directory");
869 CheckError(Status, FileName);
870 } // if (Status != EFI_NOT_FOUND)
871 } // if not scanning our own directory
872 } /* static VOID ScanLoaderDir() */
873
874 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
875 EFI_STATUS Status;
876 REFIT_DIR_ITER EfiDirIter;
877 EFI_FILE_INFO *EfiDirEntry;
878 CHAR16 FileName[256], *Directory, *MatchPatterns;
879 UINTN i, Length;
880
881 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
882 if (GlobalConfig.ScanAllLinux)
883 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
884
885 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
886 // check for Mac OS X boot loader
887 if (!IsIn(L"System\\Library\\CoreServices", GlobalConfig.DontScan)) {
888 StrCpy(FileName, MACOSX_LOADER_PATH);
889 if (FileExists(Volume->RootDir, FileName)) {
890 AddLoaderEntry(FileName, L"Mac OS X", Volume);
891 }
892
893 // check for XOM
894 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
895 if (FileExists(Volume->RootDir, FileName)) {
896 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
897 }
898 } // if Mac directory not in GlobalConfig.DontScan list
899
900 // check for Microsoft boot loader/menu
901 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
902 if (FileExists(Volume->RootDir, FileName) && !IsIn(L"EFI\\Microsoft\\Boot", GlobalConfig.DontScan)) {
903 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
904 }
905
906 // scan the root directory for EFI executables
907 ScanLoaderDir(Volume, L"\\", MatchPatterns);
908
909 // scan subdirectories of the EFI directory (as per the standard)
910 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
911 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
912 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
913 continue; // skip this, doesn't contain boot loaders
914 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
915 ScanLoaderDir(Volume, FileName, MatchPatterns);
916 } // while()
917 Status = DirIterClose(&EfiDirIter);
918 if (Status != EFI_NOT_FOUND)
919 CheckError(Status, L"while scanning the EFI directory");
920
921 // Scan user-specified (or additional default) directories....
922 i = 0;
923 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
924 CleanUpPathNameSlashes(Directory);
925 Length = StrLen(Directory);
926 if (Length > 0)
927 ScanLoaderDir(Volume, Directory, MatchPatterns);
928 FreePool(Directory);
929 } // while
930 } // if
931 } // static VOID ScanEfiFiles()
932
933 // Scan internal disks for valid EFI boot loaders....
934 static VOID ScanInternal(VOID) {
935 UINTN VolumeIndex;
936
937 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
938 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
939 ScanEfiFiles(Volumes[VolumeIndex]);
940 }
941 } // for
942 } // static VOID ScanInternal()
943
944 // Scan external disks for valid EFI boot loaders....
945 static VOID ScanExternal(VOID) {
946 UINTN VolumeIndex;
947
948 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
949 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
950 ScanEfiFiles(Volumes[VolumeIndex]);
951 }
952 } // for
953 } // static VOID ScanExternal()
954
955 // Scan internal disks for valid EFI boot loaders....
956 static VOID ScanOptical(VOID) {
957 UINTN VolumeIndex;
958
959 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
960 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
961 ScanEfiFiles(Volumes[VolumeIndex]);
962 }
963 } // for
964 } // static VOID ScanOptical()
965
966 //
967 // legacy boot functions
968 //
969
970 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
971 {
972 EFI_STATUS Status;
973 UINT8 SectorBuffer[512];
974 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
975 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
976 UINTN LogicalPartitionIndex = 4;
977 UINTN i;
978 BOOLEAN HaveBootCode;
979
980 // read MBR
981 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
982 if (EFI_ERROR(Status))
983 return Status;
984 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
985 return EFI_NOT_FOUND; // safety measure #1
986
987 // add boot code if necessary
988 HaveBootCode = FALSE;
989 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
990 if (SectorBuffer[i] != 0) {
991 HaveBootCode = TRUE;
992 break;
993 }
994 }
995 if (!HaveBootCode) {
996 // no boot code found in the MBR, add the syslinux MBR code
997 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
998 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
999 }
1000
1001 // set the partition active
1002 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1003 ExtBase = 0;
1004 for (i = 0; i < 4; i++) {
1005 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
1006 return EFI_NOT_FOUND; // safety measure #2
1007 if (i == PartitionIndex)
1008 MbrTable[i].Flags = 0x80;
1009 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
1010 MbrTable[i].Flags = 0x80;
1011 ExtBase = MbrTable[i].StartLBA;
1012 } else
1013 MbrTable[i].Flags = 0x00;
1014 }
1015
1016 // write MBR
1017 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
1018 if (EFI_ERROR(Status))
1019 return Status;
1020
1021 if (PartitionIndex >= 4) {
1022 // we have to activate a logical partition, so walk the EMBR chain
1023
1024 // NOTE: ExtBase was set above while looking at the MBR table
1025 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
1026 // read current EMBR
1027 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1028 if (EFI_ERROR(Status))
1029 return Status;
1030 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
1031 return EFI_NOT_FOUND; // safety measure #3
1032
1033 // scan EMBR, set appropriate partition active
1034 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
1035 NextExtCurrent = 0;
1036 for (i = 0; i < 4; i++) {
1037 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
1038 return EFI_NOT_FOUND; // safety measure #4
1039 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
1040 break;
1041 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
1042 // link to next EMBR
1043 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
1044 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
1045 break;
1046 } else {
1047 // logical partition
1048 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
1049 LogicalPartitionIndex++;
1050 }
1051 }
1052
1053 // write current EMBR
1054 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
1055 if (EFI_ERROR(Status))
1056 return Status;
1057
1058 if (PartitionIndex < LogicalPartitionIndex)
1059 break; // stop the loop, no need to touch further EMBRs
1060 }
1061
1062 }
1063
1064 return EFI_SUCCESS;
1065 } /* static EFI_STATUS ActivateMbrPartition() */
1066
1067 // early 2006 Core Duo / Core Solo models
1068 static UINT8 LegacyLoaderDevicePath1Data[] = {
1069 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1070 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1071 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
1072 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1073 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1074 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1075 };
1076 // mid-2006 Mac Pro (and probably other Core 2 models)
1077 static UINT8 LegacyLoaderDevicePath2Data[] = {
1078 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1079 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1080 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
1081 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1082 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1083 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1084 };
1085 // mid-2007 MBP ("Santa Rosa" based models)
1086 static UINT8 LegacyLoaderDevicePath3Data[] = {
1087 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1088 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1089 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1090 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1091 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1092 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1093 };
1094 // early-2008 MBA
1095 static UINT8 LegacyLoaderDevicePath4Data[] = {
1096 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1097 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
1098 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
1099 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1100 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1101 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1102 };
1103 // late-2008 MB/MBP (NVidia chipset)
1104 static UINT8 LegacyLoaderDevicePath5Data[] = {
1105 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
1106 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
1107 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
1108 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
1109 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
1110 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1111 };
1112
1113 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1114 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1115 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1116 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1117 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1118 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1119 NULL
1120 };
1121
1122 #define MAX_DISCOVERED_PATHS (16)
1123
1124 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1125 {
1126 EFI_STATUS Status;
1127 EG_IMAGE *BootLogoImage;
1128 UINTN ErrorInStep = 0;
1129 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1130
1131 BeginExternalScreen(TRUE, L"Booting Legacy OS");
1132
1133 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1134 if (BootLogoImage != NULL)
1135 BltImageAlpha(BootLogoImage,
1136 (UGAWidth - BootLogoImage->Width ) >> 1,
1137 (UGAHeight - BootLogoImage->Height) >> 1,
1138 &StdBackgroundPixel);
1139
1140 if (Entry->Volume->IsMbrPartition)
1141 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1142
1143 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1144
1145 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep, TRUE);
1146 if (Status == EFI_NOT_FOUND) {
1147 if (ErrorInStep == 1) {
1148 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1149 } else if (ErrorInStep == 3) {
1150 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1151 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1152 }
1153 }
1154 FinishExternalScreen();
1155 } /* static VOID StartLegacy() */
1156
1157 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1158 {
1159 LEGACY_ENTRY *Entry, *SubEntry;
1160 REFIT_MENU_SCREEN *SubScreen;
1161 CHAR16 *VolDesc;
1162 CHAR16 ShortcutLetter = 0;
1163
1164 if (LoaderTitle == NULL) {
1165 if (Volume->OSName != NULL) {
1166 LoaderTitle = Volume->OSName;
1167 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1168 ShortcutLetter = LoaderTitle[0];
1169 } else
1170 LoaderTitle = L"Legacy OS";
1171 }
1172 if (Volume->VolName != NULL)
1173 VolDesc = Volume->VolName;
1174 else
1175 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1176
1177 // prepare the menu entry
1178 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1179 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1180 SPrint(Entry->me.Title, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
1181 Entry->me.Tag = TAG_LEGACY;
1182 Entry->me.Row = 0;
1183 Entry->me.ShortcutLetter = ShortcutLetter;
1184 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1185 Entry->me.BadgeImage = Volume->VolBadgeImage;
1186 Entry->Volume = Volume;
1187 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1188 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1189 Entry->Enabled = TRUE;
1190
1191 // create the submenu
1192 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1193 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
1194 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1195 SubScreen->TitleImage = Entry->me.Image;
1196
1197 // default entry
1198 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1199 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
1200 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
1201 SubEntry->me.Tag = TAG_LEGACY;
1202 SubEntry->Volume = Entry->Volume;
1203 SubEntry->LoadOptions = Entry->LoadOptions;
1204 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1205
1206 AddMenuEntry(SubScreen, &MenuEntryReturn);
1207 Entry->me.SubScreen = SubScreen;
1208 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1209 return Entry;
1210 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1211
1212 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1213 UINTN VolumeIndex2;
1214 BOOLEAN ShowVolume, HideIfOthersFound;
1215
1216 ShowVolume = FALSE;
1217 HideIfOthersFound = FALSE;
1218 if (Volume->IsAppleLegacy) {
1219 ShowVolume = TRUE;
1220 HideIfOthersFound = TRUE;
1221 } else if (Volume->HasBootCode) {
1222 ShowVolume = TRUE;
1223 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1224 Volume->BlockIOOffset == 0 &&
1225 Volume->OSName == NULL)
1226 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1227 HideIfOthersFound = TRUE;
1228 }
1229 if (HideIfOthersFound) {
1230 // check for other bootable entries on the same disk
1231 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1232 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1233 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1234 ShowVolume = FALSE;
1235 }
1236 }
1237
1238 if (ShowVolume)
1239 AddLegacyEntry(NULL, Volume);
1240 } // static VOID ScanLegacyVolume()
1241
1242 // Scan attached optical discs for legacy (BIOS) boot code
1243 // and add anything found to the list....
1244 static VOID ScanLegacyDisc(VOID)
1245 {
1246 UINTN VolumeIndex;
1247 REFIT_VOLUME *Volume;
1248
1249 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1250 Volume = Volumes[VolumeIndex];
1251 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1252 ScanLegacyVolume(Volume, VolumeIndex);
1253 } // for
1254 } /* static VOID ScanLegacyDisc() */
1255
1256 // Scan internal hard disks for legacy (BIOS) boot code
1257 // and add anything found to the list....
1258 static VOID ScanLegacyInternal(VOID)
1259 {
1260 UINTN VolumeIndex;
1261 REFIT_VOLUME *Volume;
1262
1263 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1264 Volume = Volumes[VolumeIndex];
1265 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1266 ScanLegacyVolume(Volume, VolumeIndex);
1267 } // for
1268 } /* static VOID ScanLegacyInternal() */
1269
1270 // Scan external disks for legacy (BIOS) boot code
1271 // and add anything found to the list....
1272 static VOID ScanLegacyExternal(VOID)
1273 {
1274 UINTN VolumeIndex;
1275 REFIT_VOLUME *Volume;
1276
1277 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1278 Volume = Volumes[VolumeIndex];
1279 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1280 ScanLegacyVolume(Volume, VolumeIndex);
1281 } // for
1282 } /* static VOID ScanLegacyExternal() */
1283
1284 //
1285 // pre-boot tool functions
1286 //
1287
1288 static VOID StartTool(IN LOADER_ENTRY *Entry)
1289 {
1290 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1291 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1292 Basename(Entry->LoaderPath), NULL, TRUE);
1293 FinishExternalScreen();
1294 } /* static VOID StartTool() */
1295
1296 static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1297 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1298 {
1299 LOADER_ENTRY *Entry;
1300 CHAR16 *TitleStr = NULL;
1301
1302 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1303
1304 MergeStrings(&TitleStr, L"Start ", 0);
1305 MergeStrings(&TitleStr, LoaderTitle, 0);
1306 Entry->me.Title = TitleStr;
1307 Entry->me.Tag = TAG_TOOL;
1308 Entry->me.Row = 1;
1309 Entry->me.ShortcutLetter = ShortcutLetter;
1310 Entry->me.Image = Image;
1311 Entry->LoaderPath = StrDuplicate(LoaderPath);
1312 Entry->DevicePath = FileDevicePath(SelfLoadedImage->DeviceHandle, Entry->LoaderPath);
1313 Entry->UseGraphicsMode = UseGraphicsMode;
1314
1315 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1316 return Entry;
1317 } /* static LOADER_ENTRY * AddToolEntry() */
1318
1319 //
1320 // pre-boot driver functions
1321 //
1322
1323 static UINTN ScanDriverDir(IN CHAR16 *Path)
1324 {
1325 EFI_STATUS Status;
1326 REFIT_DIR_ITER DirIter;
1327 UINTN NumFound = 0;
1328 EFI_FILE_INFO *DirEntry;
1329 CHAR16 FileName[256];
1330
1331 CleanUpPathNameSlashes(Path);
1332 // look through contents of the directory
1333 DirIterOpen(SelfRootDir, Path, &DirIter);
1334 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1335 if (DirEntry->FileName[0] == '.')
1336 continue; // skip this
1337
1338 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1339 NumFound++;
1340 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1341 L"", DirEntry->FileName, DirEntry->FileName, NULL, FALSE);
1342 }
1343 Status = DirIterClose(&DirIter);
1344 if (Status != EFI_NOT_FOUND) {
1345 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1346 CheckError(Status, FileName);
1347 }
1348 return (NumFound);
1349 }
1350
1351 #ifdef __MAKEWITH_GNUEFI
1352 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1353 {
1354 EFI_STATUS Status;
1355 UINTN AllHandleCount;
1356 EFI_HANDLE *AllHandleBuffer;
1357 UINTN Index;
1358 UINTN HandleCount;
1359 EFI_HANDLE *HandleBuffer;
1360 UINT32 *HandleType;
1361 UINTN HandleIndex;
1362 BOOLEAN Parent;
1363 BOOLEAN Device;
1364
1365 Status = LibLocateHandle(AllHandles,
1366 NULL,
1367 NULL,
1368 &AllHandleCount,
1369 &AllHandleBuffer);
1370 if (EFI_ERROR(Status))
1371 return Status;
1372
1373 for (Index = 0; Index < AllHandleCount; Index++) {
1374 //
1375 // Scan the handle database
1376 //
1377 Status = LibScanHandleDatabase(NULL,
1378 NULL,
1379 AllHandleBuffer[Index],
1380 NULL,
1381 &HandleCount,
1382 &HandleBuffer,
1383 &HandleType);
1384 if (EFI_ERROR (Status))
1385 goto Done;
1386
1387 Device = TRUE;
1388 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1389 Device = FALSE;
1390 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1391 Device = FALSE;
1392
1393 if (Device) {
1394 Parent = FALSE;
1395 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1396 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1397 Parent = TRUE;
1398 } // for
1399
1400 if (!Parent) {
1401 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1402 Status = refit_call4_wrapper(BS->ConnectController,
1403 AllHandleBuffer[Index],
1404 NULL,
1405 NULL,
1406 TRUE);
1407 }
1408 }
1409 }
1410
1411 FreePool (HandleBuffer);
1412 FreePool (HandleType);
1413 }
1414
1415 Done:
1416 FreePool (AllHandleBuffer);
1417 return Status;
1418 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1419 #else
1420 static EFI_STATUS ConnectAllDriversToAllControllers(VOID) {
1421 BdsLibConnectAllDriversToAllControllers();
1422 return 0;
1423 }
1424 #endif
1425
1426 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1427 // directories specified by the user in the "scan_driver_dirs" configuration
1428 // file line.
1429 static VOID LoadDrivers(VOID)
1430 {
1431 CHAR16 *Directory, *SelfDirectory;
1432 UINTN i = 0, Length, NumFound = 0;
1433
1434 // load drivers from the subdirectories of rEFInd's home directory specified
1435 // in the DRIVER_DIRS constant.
1436 while ((Directory = FindCommaDelimited(DRIVER_DIRS, i++)) != NULL) {
1437 SelfDirectory = StrDuplicate(SelfDirPath);
1438 CleanUpPathNameSlashes(SelfDirectory);
1439 MergeStrings(&SelfDirectory, Directory, L'\\');
1440 NumFound += ScanDriverDir(SelfDirectory);
1441 FreePool(Directory);
1442 FreePool(SelfDirectory);
1443 }
1444
1445 // Scan additional user-specified driver directories....
1446 i = 0;
1447 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1448 CleanUpPathNameSlashes(Directory);
1449 Length = StrLen(Directory);
1450 if (Length > 0)
1451 NumFound += ScanDriverDir(Directory);
1452 FreePool(Directory);
1453 } // while
1454
1455 // connect all devices
1456 if (NumFound > 0)
1457 ConnectAllDriversToAllControllers();
1458 } /* static VOID LoadDrivers() */
1459
1460 static VOID ScanForBootloaders(VOID) {
1461 UINTN i;
1462
1463 ScanVolumes();
1464
1465 // scan for loaders and tools, add them to the menu
1466 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1467 switch(GlobalConfig.ScanFor[i]) {
1468 case 'c': case 'C':
1469 ScanLegacyDisc();
1470 break;
1471 case 'h': case 'H':
1472 ScanLegacyInternal();
1473 break;
1474 case 'b': case 'B':
1475 ScanLegacyExternal();
1476 break;
1477 case 'm': case 'M':
1478 ScanUserConfigured();
1479 break;
1480 case 'e': case 'E':
1481 ScanExternal();
1482 break;
1483 case 'i': case 'I':
1484 ScanInternal();
1485 break;
1486 case 'o': case 'O':
1487 ScanOptical();
1488 break;
1489 } // switch()
1490 } // for
1491
1492 // assign shortcut keys
1493 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1494 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1495
1496 // wait for user ACK when there were errors
1497 FinishTextScreen(FALSE);
1498 } // static VOID ScanForBootloaders()
1499
1500 // Add the second-row tags containing built-in and external tools (EFI shell,
1501 // reboot, etc.)
1502 static VOID ScanForTools(VOID) {
1503 CHAR16 *FileName = NULL;
1504 REFIT_MENU_ENTRY *TempMenuEntry;
1505 UINTN i, j;
1506
1507 for (i = 0; i < NUM_TOOLS; i++) {
1508 switch(GlobalConfig.ShowTools[i]) {
1509 case TAG_SHUTDOWN:
1510 TempMenuEntry = CopyMenuEntry(&MenuEntryShutdown);
1511 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1512 AddMenuEntry(&MainMenu, TempMenuEntry);
1513 break;
1514 case TAG_REBOOT:
1515 TempMenuEntry = CopyMenuEntry(&MenuEntryReset);
1516 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1517 AddMenuEntry(&MainMenu, TempMenuEntry);
1518 break;
1519 case TAG_ABOUT:
1520 TempMenuEntry = CopyMenuEntry(&MenuEntryAbout);
1521 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1522 AddMenuEntry(&MainMenu, TempMenuEntry);
1523 break;
1524 case TAG_EXIT:
1525 TempMenuEntry = CopyMenuEntry(&MenuEntryExit);
1526 TempMenuEntry->Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1527 AddMenuEntry(&MainMenu, TempMenuEntry);
1528 break;
1529 case TAG_SHELL:
1530 j = 0;
1531 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1532 if (FileExists(SelfRootDir, FileName)) {
1533 AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'S', FALSE);
1534 }
1535 } // while
1536 break;
1537 case TAG_GPTSYNC:
1538 MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1539 if (FileExists(SelfRootDir, FileName)) {
1540 AddToolEntry(FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1541 }
1542 break;
1543 } // switch()
1544 if (FileName != NULL) {
1545 FreePool(FileName);
1546 FileName = NULL;
1547 }
1548 } // for
1549 } // static VOID ScanForTools
1550
1551 // Rescan for boot loaders
1552 VOID RescanAll(VOID) {
1553 EG_PIXEL BGColor;
1554
1555 BGColor.b = 255;
1556 BGColor.g = 175;
1557 BGColor.r = 100;
1558 BGColor.a = 0;
1559 egDisplayMessage(L"Scanning for new boot loaders; please wait....", &BGColor);
1560 FreeList((VOID ***) &(MainMenu.Entries), &MainMenu.EntryCount);
1561 MainMenu.Entries = NULL;
1562 MainMenu.EntryCount = 0;
1563 ReadConfig();
1564 ConnectAllDriversToAllControllers();
1565 ScanForBootloaders();
1566 ScanForTools();
1567 SetupScreen();
1568 } // VOID RescanAll()
1569
1570 #ifndef __MAKEWITH_GNUEFI
1571
1572 // Minimal initialization function
1573 static VOID InitializeLib(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
1574 gST = SystemTable;
1575 // gImageHandle = ImageHandle;
1576 gBS = SystemTable->BootServices;
1577 // gRS = SystemTable->RuntimeServices;
1578 EfiGetSystemConfigurationTable (&gEfiDxeServicesTableGuid, (VOID **) &gDS);
1579
1580 InitializeConsoleSim();
1581 }
1582
1583 #endif
1584
1585 //
1586 // main entry point
1587 //
1588 EFI_STATUS
1589 EFIAPI
1590 efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
1591 {
1592 EFI_STATUS Status;
1593 BOOLEAN MainLoopRunning = TRUE;
1594 REFIT_MENU_ENTRY *ChosenEntry;
1595 UINTN MenuExit;
1596 CHAR16 *Selection;
1597
1598 // bootstrap
1599 InitializeLib(ImageHandle, SystemTable);
1600 InitScreen();
1601 Status = InitRefitLib(ImageHandle);
1602 if (EFI_ERROR(Status))
1603 return Status;
1604
1605 // read configuration
1606 CopyMem(GlobalConfig.ScanFor, "ieo ", NUM_SCAN_OPTIONS);
1607 ReadConfig();
1608 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
1609
1610 // disable EFI watchdog timer
1611 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
1612
1613 // further bootstrap (now with config available)
1614 SetupScreen();
1615 LoadDrivers();
1616 ScanForBootloaders();
1617 ScanForTools();
1618
1619 Selection = StrDuplicate(GlobalConfig.DefaultSelection);
1620 while (MainLoopRunning) {
1621 MenuExit = RunMainMenu(&MainMenu, Selection, &ChosenEntry);
1622
1623 // The Escape key triggers a re-scan operation....
1624 if (MenuExit == MENU_EXIT_ESCAPE) {
1625 RescanAll();
1626 continue;
1627 }
1628
1629 switch (ChosenEntry->Tag) {
1630
1631 case TAG_REBOOT: // Reboot
1632 TerminateScreen();
1633 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1634 MainLoopRunning = FALSE; // just in case we get this far
1635 break;
1636
1637 case TAG_SHUTDOWN: // Shut Down
1638 TerminateScreen();
1639 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1640 MainLoopRunning = FALSE; // just in case we get this far
1641 break;
1642
1643 case TAG_ABOUT: // About rEFInd
1644 AboutrEFInd();
1645 break;
1646
1647 case TAG_LOADER: // Boot OS via .EFI loader
1648 StartLoader((LOADER_ENTRY *)ChosenEntry);
1649 break;
1650
1651 case TAG_LEGACY: // Boot legacy OS
1652 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
1653 break;
1654
1655 case TAG_TOOL: // Start a EFI tool
1656 StartTool((LOADER_ENTRY *)ChosenEntry);
1657 break;
1658
1659 case TAG_EXIT: // Terminate rEFInd
1660 BeginTextScreen(L" ");
1661 return EFI_SUCCESS;
1662 break;
1663
1664 } // switch()
1665 FreePool(Selection);
1666 Selection = StrDuplicate(ChosenEntry->Title);
1667 } // while()
1668
1669 // If we end up here, things have gone wrong. Try to reboot, and if that
1670 // fails, go into an endless loop.
1671 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1672 EndlessIdleLoop();
1673
1674 return EFI_SUCCESS;
1675 } /* efi_main() */