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