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