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