]> code.delx.au - refind/blob - refind/main.c
Improved text-mode display's initial list & scrolling
[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,
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.2.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 new LOADER_ENTRY data structure and populates it with
300 // default values from the specified Entry, or NULL values if Entry
301 // is unspecified (NULL).
302 // Returns a pointer to the new data structure, or NULL if it
303 // couldn't be allocated
304 LOADER_ENTRY *InitializeLoaderEntry(IN LOADER_ENTRY *Entry) {
305 LOADER_ENTRY *NewEntry = NULL;
306
307 NewEntry = AllocateZeroPool(sizeof(LOADER_ENTRY));
308 if (NewEntry != NULL) {
309 NewEntry->me.Title = NULL;
310 NewEntry->me.Tag = TAG_LOADER;
311 NewEntry->Enabled = TRUE;
312 NewEntry->UseGraphicsMode = FALSE;
313 NewEntry->OSType = 0;
314 if (Entry != NULL) {
315 NewEntry->LoaderPath = StrDuplicate(Entry->LoaderPath);
316 NewEntry->VolName = StrDuplicate(Entry->VolName);
317 NewEntry->DevicePath = Entry->DevicePath;
318 NewEntry->UseGraphicsMode = Entry->UseGraphicsMode;
319 NewEntry->LoadOptions = StrDuplicate(Entry->LoadOptions);
320 NewEntry->InitrdPath = StrDuplicate(Entry->InitrdPath);
321 }
322 } // if
323 return (NewEntry);
324 } // LOADER_ENTRY *InitializeLoaderEntry()
325
326 // Prepare a REFIT_MENU_SCREEN data structure for a subscreen entry. This sets up
327 // the default entry that launches the boot loader using the same options as the
328 // main Entry does. Subsequent options can be added by the calling function.
329 // If a subscreen already exists in the Entry that's passed to this function,
330 // it's left unchanged and a pointer to it is returned.
331 // Returns a pointer to the new subscreen data structure, or NULL if there
332 // were problems allocating memory.
333 REFIT_MENU_SCREEN *InitializeSubScreen(IN LOADER_ENTRY *Entry) {
334 CHAR16 *FileName, *Temp;
335 REFIT_MENU_SCREEN *SubScreen = NULL;
336 LOADER_ENTRY *SubEntry;
337
338 FileName = Basename(Entry->LoaderPath);
339 if (Entry->me.SubScreen == NULL) { // No subscreen yet; initialize default entry....
340 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
341 if (SubScreen != NULL) {
342 SubScreen->Title = PoolPrint(L"Boot Options for %s on %s", (Entry->Title != NULL) ? Entry->Title : FileName, Entry->VolName);
343 SubScreen->TitleImage = Entry->me.Image;
344 // default entry
345 SubEntry = InitializeLoaderEntry(Entry);
346 if (SubEntry != NULL) {
347 SubEntry->me.Title = L"Boot using default options";
348 if ((SubEntry->InitrdPath != NULL) && (StrLen(SubEntry->InitrdPath) > 0) && (!StriSubCmp(L"initrd", SubEntry->LoadOptions))) {
349 Temp = PoolPrint(L"initrd=%s", SubEntry->InitrdPath);
350 MergeStrings(&SubEntry->LoadOptions, Temp, L' ');
351 FreePool(Temp);
352 } // if
353 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
354 } // if (SubEntry != NULL)
355 } // if (SubScreen != NULL)
356 } else { // existing subscreen; less initialization, and just add new entry later....
357 SubScreen = Entry->me.SubScreen;
358 } // if/else
359 return SubScreen;
360 } // REFIT_MENU_SCREEN *InitializeSubScreen()
361
362 VOID GenerateSubScreen(LOADER_ENTRY *Entry, IN REFIT_VOLUME *Volume) {
363 REFIT_MENU_SCREEN *SubScreen;
364 LOADER_ENTRY *SubEntry;
365 CHAR16 *FileName, *InitrdOption = NULL, *Temp;
366 CHAR16 DiagsFileName[256];
367 REFIT_FILE *File;
368 UINTN TokenCount;
369 CHAR16 **TokenList;
370
371 FileName = Basename(Entry->LoaderPath);
372 // create the submenu
373 if (StrLen(Entry->Title) == 0) {
374 FreePool(Entry->Title);
375 Entry->Title = NULL;
376 }
377 SubScreen = InitializeSubScreen(Entry);
378
379 // loader-specific submenu entries
380 if (Entry->OSType == 'M') { // entries for Mac OS X
381 #if defined(EFIX64)
382 SubEntry = InitializeLoaderEntry(Entry);
383 if (SubEntry != NULL) {
384 SubEntry->me.Title = L"Boot Mac OS X with a 64-bit kernel";
385 SubEntry->LoadOptions = L"arch=x86_64";
386 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
387 } // if
388
389 SubEntry = InitializeLoaderEntry(Entry);
390 if (SubEntry != NULL) {
391 SubEntry->me.Title = L"Boot Mac OS X with a 32-bit kernel";
392 SubEntry->LoadOptions = L"arch=i386";
393 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
394 } // if
395 #endif
396
397 if (!(GlobalConfig.HideUIFlags & HIDEUI_FLAG_SINGLEUSER)) {
398 SubEntry = InitializeLoaderEntry(Entry);
399 if (SubEntry != NULL) {
400 SubEntry->me.Title = L"Boot Mac OS X in verbose mode";
401 SubEntry->UseGraphicsMode = FALSE;
402 SubEntry->LoadOptions = L"-v";
403 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
404 } // if
405
406 #if defined(EFIX64)
407 SubEntry = InitializeLoaderEntry(Entry);
408 if (SubEntry != NULL) {
409 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (64-bit)";
410 SubEntry->UseGraphicsMode = FALSE;
411 SubEntry->LoadOptions = L"-v arch=x86_64";
412 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
413 }
414
415 SubEntry = InitializeLoaderEntry(Entry);
416 if (SubEntry != NULL) {
417 SubEntry->me.Title = L"Boot Mac OS X in verbose mode (32-bit)";
418 SubEntry->UseGraphicsMode = FALSE;
419 SubEntry->LoadOptions = L"-v arch=i386";
420 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
421 }
422 #endif
423
424 SubEntry = InitializeLoaderEntry(Entry);
425 if (SubEntry != NULL) {
426 SubEntry->me.Title = L"Boot Mac OS X in single user mode";
427 SubEntry->UseGraphicsMode = FALSE;
428 SubEntry->LoadOptions = L"-v -s";
429 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
430 } // if
431 } // not single-user
432
433 // check for Apple hardware diagnostics
434 StrCpy(DiagsFileName, L"\\System\\Library\\CoreServices\\.diagnostics\\diags.efi");
435 if (FileExists(Volume->RootDir, DiagsFileName) && !(GlobalConfig.HideUIFlags & HIDEUI_FLAG_HWTEST)) {
436 SubEntry = InitializeLoaderEntry(Entry);
437 if (SubEntry != NULL) {
438 SubEntry->me.Title = L"Run Apple Hardware Test";
439 FreePool(SubEntry->LoaderPath);
440 SubEntry->LoaderPath = StrDuplicate(DiagsFileName);
441 SubEntry->DevicePath = FileDevicePath(Volume->DeviceHandle, SubEntry->LoaderPath);
442 SubEntry->UseGraphicsMode = TRUE;
443 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
444 } // if
445 } // if diagnostics entry found
446
447 } else if (Entry->OSType == 'L') { // entries for Linux kernels with EFI stub loaders
448 File = ReadLinuxOptionsFile(Entry->LoaderPath, Volume);
449 if (File != NULL) {
450 if ((Temp = FindInitrd(Entry->LoaderPath, Volume)) != NULL)
451 InitrdOption = PoolPrint(L"initrd=%s", Temp);
452 TokenCount = ReadTokenLine(File, &TokenList); // read and discard first entry, since it's
453 FreeTokenLine(&TokenList, &TokenCount); // set up by InitializeSubScreen(), earlier....
454 while ((TokenCount = ReadTokenLine(File, &TokenList)) > 1) {
455 SubEntry = InitializeLoaderEntry(Entry);
456 SubEntry->me.Title = StrDuplicate(TokenList[0]);
457 if (SubEntry->LoadOptions != NULL)
458 FreePool(SubEntry->LoadOptions);
459 SubEntry->LoadOptions = StrDuplicate(TokenList[1]);
460 MergeStrings(&SubEntry->LoadOptions, InitrdOption, L' ');
461 FreeTokenLine(&TokenList, &TokenCount);
462 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
463 } // while
464 if (InitrdOption)
465 FreePool(InitrdOption);
466 if (Temp)
467 FreePool(Temp);
468 FreePool(File);
469 } // if Linux options file exists
470
471 } else if (Entry->OSType == 'E') { // entries for ELILO
472 SubEntry = InitializeLoaderEntry(Entry);
473 if (SubEntry != NULL) {
474 SubEntry->me.Title = PoolPrint(L"Run %s in interactive mode", FileName);
475 SubEntry->LoadOptions = L"-p";
476 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
477 }
478
479 SubEntry = InitializeLoaderEntry(Entry);
480 if (SubEntry != NULL) {
481 SubEntry->me.Title = L"Boot Linux for a 17\" iMac or a 15\" MacBook Pro (*)";
482 SubEntry->UseGraphicsMode = TRUE;
483 SubEntry->LoadOptions = L"-d 0 i17";
484 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
485 }
486
487 SubEntry = InitializeLoaderEntry(Entry);
488 if (SubEntry != NULL) {
489 SubEntry->me.Title = L"Boot Linux for a 20\" iMac (*)";
490 SubEntry->UseGraphicsMode = TRUE;
491 SubEntry->LoadOptions = L"-d 0 i20";
492 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
493 }
494
495 SubEntry = InitializeLoaderEntry(Entry);
496 if (SubEntry != NULL) {
497 SubEntry->me.Title = L"Boot Linux for a Mac Mini (*)";
498 SubEntry->UseGraphicsMode = TRUE;
499 SubEntry->LoadOptions = L"-d 0 mini";
500 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
501 }
502
503 AddMenuInfoLine(SubScreen, L"NOTE: This is an example. Entries");
504 AddMenuInfoLine(SubScreen, L"marked with (*) may not work.");
505
506 } else if (Entry->OSType == 'X') { // entries for xom.efi
507 // by default, skip the built-in selection and boot from hard disk only
508 Entry->LoadOptions = L"-s -h";
509
510 SubEntry = InitializeLoaderEntry(Entry);
511 if (SubEntry != NULL) {
512 SubEntry->me.Title = L"Boot Windows from Hard Disk";
513 SubEntry->LoadOptions = L"-s -h";
514 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
515 }
516
517 SubEntry = InitializeLoaderEntry(Entry);
518 if (SubEntry != NULL) {
519 SubEntry->me.Title = L"Boot Windows from CD-ROM";
520 SubEntry->LoadOptions = L"-s -c";
521 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
522 }
523
524 SubEntry = InitializeLoaderEntry(Entry);
525 if (SubEntry != NULL) {
526 SubEntry->me.Title = PoolPrint(L"Run %s in text mode", FileName);
527 SubEntry->UseGraphicsMode = FALSE;
528 SubEntry->LoadOptions = L"-v";
529 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
530 }
531 } // entries for xom.efi
532 AddMenuEntry(SubScreen, &MenuEntryReturn);
533 Entry->me.SubScreen = SubScreen;
534 } // VOID GenerateSubScreen()
535
536 // Returns options for a Linux kernel. Reads them from an options file in the
537 // kernel's directory; and if present, adds an initrd= option for an initial
538 // RAM disk file with the same version number as the kernel file.
539 static CHAR16 * GetMainLinuxOptions(IN CHAR16 * LoaderPath, IN REFIT_VOLUME *Volume) {
540 CHAR16 *Options = NULL, *InitrdName, *InitrdOption = NULL;
541
542 Options = GetFirstOptionsFromFile(LoaderPath, Volume);
543 InitrdName = FindInitrd(LoaderPath, Volume);
544 if (InitrdName != NULL)
545 InitrdOption = PoolPrint(L"initrd=%s", InitrdName);
546 MergeStrings(&Options, InitrdOption, ' ');
547 if (InitrdOption != NULL)
548 FreePool(InitrdOption);
549 if (InitrdName != NULL)
550 FreePool(InitrdName);
551 return (Options);
552 } // static CHAR16 * GetMainLinuxOptions()
553
554 // Sets a few defaults for a loader entry -- mainly the icon, but also the OS type
555 // code and shortcut letter. For Linux EFI stub loaders, also sets kernel options
556 // that will (with luck) work fairly automatically.
557 VOID SetLoaderDefaults(LOADER_ENTRY *Entry, CHAR16 *LoaderPath, IN REFIT_VOLUME *Volume) {
558 CHAR16 IconFileName[256];
559 CHAR16 *FileName, *PathOnly, *OSIconName = NULL, *Temp;
560 CHAR16 ShortcutLetter = 0;
561
562 FileName = Basename(LoaderPath);
563 PathOnly = FindPath(LoaderPath);
564
565 // locate a custom icon for the loader
566 StrCpy(IconFileName, LoaderPath);
567 ReplaceEfiExtension(IconFileName, L".icns");
568 if (FileExists(Volume->RootDir, IconFileName)) {
569 Entry->me.Image = LoadIcns(Volume->RootDir, IconFileName, 128);
570 } else if ((StrLen(PathOnly) == 0) && (Volume->VolIconImage != NULL)) {
571 Entry->me.Image = Volume->VolIconImage;
572 } // icon matched to loader or volume
573
574 Temp = FindLastDirName(LoaderPath);
575 MergeStrings(&OSIconName, Temp, L',');
576 FreePool(Temp);
577 if (OSIconName != NULL) {
578 ShortcutLetter = OSIconName[0];
579 }
580
581 // detect specific loaders
582 if (StriSubCmp(L"bzImage", LoaderPath) || StriSubCmp(L"vmlinuz", LoaderPath)) {
583 MergeStrings(&OSIconName, L"linux", L',');
584 Entry->OSType = 'L';
585 if (ShortcutLetter == 0)
586 ShortcutLetter = 'L';
587 Entry->LoadOptions = GetMainLinuxOptions(LoaderPath, Volume);
588 } else if (StriSubCmp(L"refit", LoaderPath)) {
589 MergeStrings(&OSIconName, L"refit", L',');
590 Entry->OSType = 'R';
591 ShortcutLetter = 'R';
592 } else if (StriCmp(LoaderPath, MACOSX_LOADER_PATH) == 0) {
593 if (Volume->VolIconImage != NULL) { // custom icon file found
594 Entry->me.Image = Volume->VolIconImage;
595 }
596 MergeStrings(&OSIconName, L"mac", L',');
597 Entry->UseGraphicsMode = TRUE;
598 Entry->OSType = 'M';
599 ShortcutLetter = 'M';
600 } else if (StriCmp(FileName, L"diags.efi") == 0) {
601 MergeStrings(&OSIconName, L"hwtest", L',');
602 } else if (StriCmp(FileName, L"e.efi") == 0 || StriCmp(FileName, L"elilo.efi") == 0) {
603 MergeStrings(&OSIconName, L"elilo,linux", L',');
604 Entry->OSType = 'E';
605 if (ShortcutLetter == 0)
606 ShortcutLetter = 'L';
607 } else if (StriCmp(FileName, L"cdboot.efi") == 0 ||
608 StriCmp(FileName, L"bootmgr.efi") == 0 ||
609 StriCmp(FileName, L"Bootmgfw.efi") == 0) {
610 MergeStrings(&OSIconName, L"win", L',');
611 Entry->OSType = 'W';
612 ShortcutLetter = 'W';
613 } else if (StriCmp(FileName, L"xom.efi") == 0) {
614 MergeStrings(&OSIconName, L"xom,win", L',');
615 Entry->UseGraphicsMode = TRUE;
616 Entry->OSType = 'X';
617 ShortcutLetter = 'W';
618 }
619
620 if ((ShortcutLetter >= 'a') && (ShortcutLetter <= 'z'))
621 ShortcutLetter = ShortcutLetter - 'a' + 'A'; // convert lowercase to uppercase
622 Entry->me.ShortcutLetter = ShortcutLetter;
623 if (Entry->me.Image == NULL)
624 Entry->me.Image = LoadOSIcon(OSIconName, L"unknown", FALSE);
625 if (PathOnly != NULL)
626 FreePool(PathOnly);
627 } // VOID SetLoaderDefaults()
628
629 // Add a specified EFI boot loader to the list, using automatic settings
630 // for icons, options, etc.
631 LOADER_ENTRY * AddLoaderEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume) {
632 LOADER_ENTRY *Entry;
633
634 CleanUpPathNameSlashes(LoaderPath);
635 Entry = InitializeLoaderEntry(NULL);
636 if (Entry != NULL) {
637 Entry->Title = StrDuplicate((LoaderTitle != NULL) ? LoaderTitle : LoaderPath);
638 // Entry->Title = StrDuplicate(LoaderTitle);
639 Entry->me.Title = PoolPrint(L"Boot %s from %s", (LoaderTitle != NULL) ? LoaderTitle : LoaderPath, Volume->VolName);
640 Entry->me.Row = 0;
641 Entry->me.BadgeImage = Volume->VolBadgeImage;
642 Entry->LoaderPath = StrDuplicate(LoaderPath);
643 Entry->VolName = Volume->VolName;
644 Entry->DevicePath = FileDevicePath(Volume->DeviceHandle, Entry->LoaderPath);
645 SetLoaderDefaults(Entry, LoaderPath, Volume);
646 GenerateSubScreen(Entry, Volume);
647 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
648 }
649
650 return(Entry);
651 } // LOADER_ENTRY * AddLoaderEntry()
652
653 // Returns -1 if (Time1 < Time2), +1 if (Time1 > Time2), or 0 if
654 // (Time1 == Time2). Precision is only to the nearest second; since
655 // this is used for sorting boot loader entries, differences smaller
656 // than this are likely to be meaningless (and unlikely!).
657 INTN TimeComp(EFI_TIME *Time1, EFI_TIME *Time2) {
658 INT64 Time1InSeconds, Time2InSeconds;
659
660 // Following values are overestimates; I'm assuming 31 days in every month.
661 // This is fine for the purpose of this function, which has a limited
662 // purpose.
663 Time1InSeconds = Time1->Second + (Time1->Minute * 60) + (Time1->Hour * 3600) + (Time1->Day * 86400) +
664 (Time1->Month * 2678400) + ((Time1->Year - 1998) * 32140800);
665 Time2InSeconds = Time2->Second + (Time2->Minute * 60) + (Time2->Hour * 3600) + (Time2->Day * 86400) +
666 (Time2->Month * 2678400) + ((Time2->Year - 1998) * 32140800);
667 if (Time1InSeconds < Time2InSeconds)
668 return (-1);
669 else if (Time1InSeconds > Time2InSeconds)
670 return (1);
671
672 return 0;
673 } // INTN TimeComp()
674
675 // Adds a loader list element, keeping it sorted by date. Returns the new
676 // first element (the one with the most recent date).
677 static struct LOADER_LIST * AddLoaderListEntry(struct LOADER_LIST *LoaderList, struct LOADER_LIST *NewEntry) {
678 struct LOADER_LIST *LatestEntry, *CurrentEntry, *PrevEntry = NULL;
679
680 LatestEntry = CurrentEntry = LoaderList;
681 if (LoaderList == NULL) {
682 LatestEntry = NewEntry;
683 } else {
684 while ((CurrentEntry != NULL) && (TimeComp(&(NewEntry->TimeStamp), &(CurrentEntry->TimeStamp)) < 0)) {
685 PrevEntry = CurrentEntry;
686 CurrentEntry = CurrentEntry->NextEntry;
687 } // while
688 NewEntry->NextEntry = CurrentEntry;
689 if (PrevEntry == NULL) {
690 LatestEntry = NewEntry;
691 } else {
692 PrevEntry->NextEntry = NewEntry;
693 } // if/else
694 } // if/else
695 return (LatestEntry);
696 } // static VOID AddLoaderListEntry()
697
698 // Delete the LOADER_LIST linked list
699 static VOID CleanUpLoaderList(struct LOADER_LIST *LoaderList) {
700 struct LOADER_LIST *Temp;
701
702 while (LoaderList != NULL) {
703 Temp = LoaderList;
704 LoaderList = LoaderList->NextEntry;
705 FreePool(Temp->FileName);
706 FreePool(Temp);
707 } // while
708 } // static VOID CleanUpLoaderList()
709
710 // Scan an individual directory for EFI boot loader files and, if found,
711 // add them to the list. Sorts the entries within the loader directory
712 // so that the most recent one appears first in the list.
713 static VOID ScanLoaderDir(IN REFIT_VOLUME *Volume, IN CHAR16 *Path, IN CHAR16 *Pattern)
714 {
715 EFI_STATUS Status;
716 REFIT_DIR_ITER DirIter;
717 EFI_FILE_INFO *DirEntry;
718 CHAR16 FileName[256], *Extension;
719 struct LOADER_LIST *LoaderList = NULL, *NewLoader;
720
721 if (!SelfDirPath || !Path || ((StriCmp(Path, SelfDirPath) == 0) && Volume != SelfVolume) ||
722 (StriCmp(Path, SelfDirPath) != 0)) {
723 // look through contents of the directory
724 DirIterOpen(Volume->RootDir, Path, &DirIter);
725 while (DirIterNext(&DirIter, 2, Pattern, &DirEntry)) {
726 Extension = FindExtension(DirEntry->FileName);
727 if (DirEntry->FileName[0] == '.' ||
728 StriCmp(DirEntry->FileName, L"TextMode.efi") == 0 ||
729 StriCmp(DirEntry->FileName, L"ebounce.efi") == 0 ||
730 StriCmp(DirEntry->FileName, L"GraphicsConsole.efi") == 0 ||
731 StriCmp(Extension, L".icns") == 0 ||
732 StriSubCmp(L"shell", DirEntry->FileName))
733 continue; // skip this
734
735 if (Path)
736 SPrint(FileName, 255, L"\\%s\\%s", Path, DirEntry->FileName);
737 else
738 SPrint(FileName, 255, L"\\%s", DirEntry->FileName);
739 CleanUpPathNameSlashes(FileName);
740 NewLoader = AllocateZeroPool(sizeof(struct LOADER_LIST));
741 if (NewLoader != NULL) {
742 NewLoader->FileName = StrDuplicate(FileName);
743 NewLoader->TimeStamp = DirEntry->ModificationTime;
744 LoaderList = AddLoaderListEntry(LoaderList, NewLoader);
745 } // if
746 FreePool(Extension);
747 } // while
748 NewLoader = LoaderList;
749 while (NewLoader != NULL) {
750 AddLoaderEntry(NewLoader->FileName, NULL, Volume);
751 NewLoader = NewLoader->NextEntry;
752 } // while
753 CleanUpLoaderList(LoaderList);
754 Status = DirIterClose(&DirIter);
755 if (Status != EFI_NOT_FOUND) {
756 if (Path)
757 SPrint(FileName, 255, L"while scanning the %s directory", Path);
758 else
759 StrCpy(FileName, L"while scanning the root directory");
760 CheckError(Status, FileName);
761 } // if (Status != EFI_NOT_FOUND)
762 } // if not scanning our own directory
763 } /* static VOID ScanLoaderDir() */
764
765 static VOID ScanEfiFiles(REFIT_VOLUME *Volume) {
766 EFI_STATUS Status;
767 REFIT_DIR_ITER EfiDirIter;
768 EFI_FILE_INFO *EfiDirEntry;
769 CHAR16 FileName[256], *Directory, *MatchPatterns;
770 UINTN i, Length;
771
772 MatchPatterns = StrDuplicate(LOADER_MATCH_PATTERNS);
773 if (GlobalConfig.ScanAllLinux)
774 MergeStrings(&MatchPatterns, LINUX_MATCH_PATTERNS, L',');
775
776 if ((Volume->RootDir != NULL) && (Volume->VolName != NULL)) {
777 // check for Mac OS X boot loader
778 StrCpy(FileName, MACOSX_LOADER_PATH);
779 if (FileExists(Volume->RootDir, FileName)) {
780 AddLoaderEntry(FileName, L"Mac OS X", Volume);
781 }
782
783 // check for XOM
784 StrCpy(FileName, L"System\\Library\\CoreServices\\xom.efi");
785 if (FileExists(Volume->RootDir, FileName)) {
786 AddLoaderEntry(FileName, L"Windows XP (XoM)", Volume);
787 }
788
789 // check for Microsoft boot loader/menu
790 StrCpy(FileName, L"EFI\\Microsoft\\Boot\\Bootmgfw.efi");
791 if (FileExists(Volume->RootDir, FileName)) {
792 AddLoaderEntry(FileName, L"Microsoft EFI boot", Volume);
793 }
794
795 // scan the root directory for EFI executables
796 ScanLoaderDir(Volume, L"\\", MatchPatterns);
797
798 // scan subdirectories of the EFI directory (as per the standard)
799 DirIterOpen(Volume->RootDir, L"EFI", &EfiDirIter);
800 while (DirIterNext(&EfiDirIter, 1, NULL, &EfiDirEntry)) {
801 if (StriCmp(EfiDirEntry->FileName, L"tools") == 0 || EfiDirEntry->FileName[0] == '.')
802 continue; // skip this, doesn't contain boot loaders
803 SPrint(FileName, 255, L"EFI\\%s", EfiDirEntry->FileName);
804 ScanLoaderDir(Volume, FileName, MatchPatterns);
805 } // while()
806 Status = DirIterClose(&EfiDirIter);
807 if (Status != EFI_NOT_FOUND)
808 CheckError(Status, L"while scanning the EFI directory");
809
810 // Scan user-specified (or additional default) directories....
811 i = 0;
812 while ((Directory = FindCommaDelimited(GlobalConfig.AlsoScan, i++)) != NULL) {
813 CleanUpPathNameSlashes(Directory);
814 Length = StrLen(Directory);
815 if (Length > 0)
816 ScanLoaderDir(Volume, Directory, MatchPatterns);
817 FreePool(Directory);
818 } // while
819 } // if
820 } // static VOID ScanEfiFiles()
821
822 // Scan internal disks for valid EFI boot loaders....
823 static VOID ScanInternal(VOID) {
824 UINTN VolumeIndex;
825
826 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
827 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_INTERNAL) {
828 ScanEfiFiles(Volumes[VolumeIndex]);
829 }
830 } // for
831 } // static VOID ScanInternal()
832
833 // Scan external disks for valid EFI boot loaders....
834 static VOID ScanExternal(VOID) {
835 UINTN VolumeIndex;
836
837 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
838 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_EXTERNAL) {
839 ScanEfiFiles(Volumes[VolumeIndex]);
840 }
841 } // for
842 } // static VOID ScanExternal()
843
844 // Scan internal disks for valid EFI boot loaders....
845 static VOID ScanOptical(VOID) {
846 UINTN VolumeIndex;
847
848 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
849 if (Volumes[VolumeIndex]->DiskKind == DISK_KIND_OPTICAL) {
850 ScanEfiFiles(Volumes[VolumeIndex]);
851 }
852 } // for
853 } // static VOID ScanOptical()
854
855 //
856 // legacy boot functions
857 //
858
859 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
860 {
861 EFI_STATUS Status;
862 UINT8 SectorBuffer[512];
863 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
864 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
865 UINTN LogicalPartitionIndex = 4;
866 UINTN i;
867 BOOLEAN HaveBootCode;
868
869 // read MBR
870 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
871 if (EFI_ERROR(Status))
872 return Status;
873 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
874 return EFI_NOT_FOUND; // safety measure #1
875
876 // add boot code if necessary
877 HaveBootCode = FALSE;
878 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
879 if (SectorBuffer[i] != 0) {
880 HaveBootCode = TRUE;
881 break;
882 }
883 }
884 if (!HaveBootCode) {
885 // no boot code found in the MBR, add the syslinux MBR code
886 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
887 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
888 }
889
890 // set the partition active
891 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
892 ExtBase = 0;
893 for (i = 0; i < 4; i++) {
894 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
895 return EFI_NOT_FOUND; // safety measure #2
896 if (i == PartitionIndex)
897 MbrTable[i].Flags = 0x80;
898 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
899 MbrTable[i].Flags = 0x80;
900 ExtBase = MbrTable[i].StartLBA;
901 } else
902 MbrTable[i].Flags = 0x00;
903 }
904
905 // write MBR
906 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
907 if (EFI_ERROR(Status))
908 return Status;
909
910 if (PartitionIndex >= 4) {
911 // we have to activate a logical partition, so walk the EMBR chain
912
913 // NOTE: ExtBase was set above while looking at the MBR table
914 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
915 // read current EMBR
916 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
917 if (EFI_ERROR(Status))
918 return Status;
919 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
920 return EFI_NOT_FOUND; // safety measure #3
921
922 // scan EMBR, set appropriate partition active
923 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
924 NextExtCurrent = 0;
925 for (i = 0; i < 4; i++) {
926 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
927 return EFI_NOT_FOUND; // safety measure #4
928 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
929 break;
930 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
931 // link to next EMBR
932 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
933 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
934 break;
935 } else {
936 // logical partition
937 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
938 LogicalPartitionIndex++;
939 }
940 }
941
942 // write current EMBR
943 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
944 if (EFI_ERROR(Status))
945 return Status;
946
947 if (PartitionIndex < LogicalPartitionIndex)
948 break; // stop the loop, no need to touch further EMBRs
949 }
950
951 }
952
953 return EFI_SUCCESS;
954 } /* static EFI_STATUS ActivateMbrPartition() */
955
956 // early 2006 Core Duo / Core Solo models
957 static UINT8 LegacyLoaderDevicePath1Data[] = {
958 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
959 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
960 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
961 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
962 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
963 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
964 };
965 // mid-2006 Mac Pro (and probably other Core 2 models)
966 static UINT8 LegacyLoaderDevicePath2Data[] = {
967 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
968 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
969 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
970 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
971 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
972 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
973 };
974 // mid-2007 MBP ("Santa Rosa" based models)
975 static UINT8 LegacyLoaderDevicePath3Data[] = {
976 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
977 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
978 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
979 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
980 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
981 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
982 };
983 // early-2008 MBA
984 static UINT8 LegacyLoaderDevicePath4Data[] = {
985 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
986 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
987 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
988 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
989 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
990 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
991 };
992 // late-2008 MB/MBP (NVidia chipset)
993 static UINT8 LegacyLoaderDevicePath5Data[] = {
994 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
995 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
996 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
997 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
998 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
999 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
1000 };
1001
1002 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
1003 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
1004 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
1005 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
1006 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
1007 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
1008 NULL
1009 };
1010
1011 #define MAX_DISCOVERED_PATHS (16)
1012
1013 static VOID StartLegacy(IN LEGACY_ENTRY *Entry)
1014 {
1015 EFI_STATUS Status;
1016 EG_IMAGE *BootLogoImage;
1017 UINTN ErrorInStep = 0;
1018 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
1019
1020 BeginExternalScreen(TRUE, L"Booting Legacy OS");
1021
1022 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
1023 if (BootLogoImage != NULL)
1024 BltImageAlpha(BootLogoImage,
1025 (UGAWidth - BootLogoImage->Width ) >> 1,
1026 (UGAHeight - BootLogoImage->Height) >> 1,
1027 &StdBackgroundPixel);
1028
1029 if (Entry->Volume->IsMbrPartition)
1030 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
1031
1032 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
1033
1034 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, NULL, L"legacy loader", &ErrorInStep, TRUE);
1035 if (Status == EFI_NOT_FOUND) {
1036 if (ErrorInStep == 1) {
1037 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
1038 } else if (ErrorInStep == 3) {
1039 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
1040 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
1041 }
1042 }
1043 FinishExternalScreen();
1044 } /* static VOID StartLegacy() */
1045
1046 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
1047 {
1048 LEGACY_ENTRY *Entry, *SubEntry;
1049 REFIT_MENU_SCREEN *SubScreen;
1050 CHAR16 *VolDesc;
1051 CHAR16 ShortcutLetter = 0;
1052
1053 if (LoaderTitle == NULL) {
1054 if (Volume->OSName != NULL) {
1055 LoaderTitle = Volume->OSName;
1056 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
1057 ShortcutLetter = LoaderTitle[0];
1058 } else
1059 LoaderTitle = L"Legacy OS";
1060 }
1061 if (Volume->VolName != NULL)
1062 VolDesc = Volume->VolName;
1063 else
1064 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
1065
1066 // prepare the menu entry
1067 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1068 Entry->me.Title = PoolPrint(L"Boot %s from %s", LoaderTitle, VolDesc);
1069 Entry->me.Tag = TAG_LEGACY;
1070 Entry->me.Row = 0;
1071 Entry->me.ShortcutLetter = ShortcutLetter;
1072 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
1073 Entry->me.BadgeImage = Volume->VolBadgeImage;
1074 Entry->Volume = Volume;
1075 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
1076 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
1077 Entry->Enabled = TRUE;
1078
1079 // create the submenu
1080 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
1081 SubScreen->Title = PoolPrint(L"Boot Options for %s on %s", LoaderTitle, VolDesc);
1082 SubScreen->TitleImage = Entry->me.Image;
1083
1084 // default entry
1085 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
1086 SubEntry->me.Title = PoolPrint(L"Boot %s", LoaderTitle);
1087 SubEntry->me.Tag = TAG_LEGACY;
1088 SubEntry->Volume = Entry->Volume;
1089 SubEntry->LoadOptions = Entry->LoadOptions;
1090 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
1091
1092 AddMenuEntry(SubScreen, &MenuEntryReturn);
1093 Entry->me.SubScreen = SubScreen;
1094 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1095 return Entry;
1096 } /* static LEGACY_ENTRY * AddLegacyEntry() */
1097
1098 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
1099 UINTN VolumeIndex2;
1100 BOOLEAN ShowVolume, HideIfOthersFound;
1101
1102 ShowVolume = FALSE;
1103 HideIfOthersFound = FALSE;
1104 if (Volume->IsAppleLegacy) {
1105 ShowVolume = TRUE;
1106 HideIfOthersFound = TRUE;
1107 } else if (Volume->HasBootCode) {
1108 ShowVolume = TRUE;
1109 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
1110 Volume->BlockIOOffset == 0 &&
1111 Volume->OSName == NULL)
1112 // this is a whole disk (MBR) entry; hide if we have entries for partitions
1113 HideIfOthersFound = TRUE;
1114 }
1115 if (HideIfOthersFound) {
1116 // check for other bootable entries on the same disk
1117 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
1118 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
1119 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
1120 ShowVolume = FALSE;
1121 }
1122 }
1123
1124 if (ShowVolume)
1125 AddLegacyEntry(NULL, Volume);
1126 } // static VOID ScanLegacyVolume()
1127
1128 // Scan attached optical discs for legacy (BIOS) boot code
1129 // and add anything found to the list....
1130 static VOID ScanLegacyDisc(VOID)
1131 {
1132 UINTN VolumeIndex;
1133 REFIT_VOLUME *Volume;
1134
1135 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1136 Volume = Volumes[VolumeIndex];
1137 if (Volume->DiskKind == DISK_KIND_OPTICAL)
1138 ScanLegacyVolume(Volume, VolumeIndex);
1139 } // for
1140 } /* static VOID ScanLegacyDisc() */
1141
1142 // Scan internal hard disks for legacy (BIOS) boot code
1143 // and add anything found to the list....
1144 static VOID ScanLegacyInternal(VOID)
1145 {
1146 UINTN VolumeIndex;
1147 REFIT_VOLUME *Volume;
1148
1149 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1150 Volume = Volumes[VolumeIndex];
1151 if (Volume->DiskKind == DISK_KIND_INTERNAL)
1152 ScanLegacyVolume(Volume, VolumeIndex);
1153 } // for
1154 } /* static VOID ScanLegacyInternal() */
1155
1156 // Scan external disks for legacy (BIOS) boot code
1157 // and add anything found to the list....
1158 static VOID ScanLegacyExternal(VOID)
1159 {
1160 UINTN VolumeIndex;
1161 REFIT_VOLUME *Volume;
1162
1163 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
1164 Volume = Volumes[VolumeIndex];
1165 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
1166 ScanLegacyVolume(Volume, VolumeIndex);
1167 } // for
1168 } /* static VOID ScanLegacyExternal() */
1169
1170 //
1171 // pre-boot tool functions
1172 //
1173
1174 static VOID StartTool(IN LOADER_ENTRY *Entry)
1175 {
1176 BeginExternalScreen(Entry->UseGraphicsMode, Entry->me.Title + 6); // assumes "Start <title>" as assigned below
1177 StartEFIImage(Entry->DevicePath, Entry->LoadOptions, Basename(Entry->LoaderPath),
1178 Basename(Entry->LoaderPath), NULL, TRUE);
1179 FinishExternalScreen();
1180 } /* static VOID StartTool() */
1181
1182 static LOADER_ENTRY * AddToolEntry(IN CHAR16 *LoaderPath, IN CHAR16 *LoaderTitle, IN EG_IMAGE *Image,
1183 IN CHAR16 ShortcutLetter, IN BOOLEAN UseGraphicsMode)
1184 {
1185 LOADER_ENTRY *Entry;
1186
1187 Entry = AllocateZeroPool(sizeof(LOADER_ENTRY));
1188
1189 Entry->me.Title = PoolPrint(L"Start %s", LoaderTitle);
1190 Entry->me.Tag = TAG_TOOL;
1191 Entry->me.Row = 1;
1192 Entry->me.ShortcutLetter = ShortcutLetter;
1193 Entry->me.Image = Image;
1194 Entry->LoaderPath = StrDuplicate(LoaderPath);
1195 Entry->DevicePath = FileDevicePath(SelfLoadedImage->DeviceHandle, Entry->LoaderPath);
1196 Entry->UseGraphicsMode = UseGraphicsMode;
1197
1198 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
1199 return Entry;
1200 } /* static LOADER_ENTRY * AddToolEntry() */
1201
1202 //
1203 // pre-boot driver functions
1204 //
1205
1206 static UINTN ScanDriverDir(IN CHAR16 *Path)
1207 {
1208 EFI_STATUS Status;
1209 REFIT_DIR_ITER DirIter;
1210 UINTN NumFound = 0;
1211 EFI_FILE_INFO *DirEntry;
1212 CHAR16 FileName[256];
1213
1214 CleanUpPathNameSlashes(Path);
1215 // look through contents of the directory
1216 DirIterOpen(SelfRootDir, Path, &DirIter);
1217 while (DirIterNext(&DirIter, 2, LOADER_MATCH_PATTERNS, &DirEntry)) {
1218 if (DirEntry->FileName[0] == '.')
1219 continue; // skip this
1220
1221 SPrint(FileName, 255, L"%s\\%s", Path, DirEntry->FileName);
1222 NumFound++;
1223 Status = StartEFIImage(FileDevicePath(SelfLoadedImage->DeviceHandle, FileName),
1224 L"", DirEntry->FileName, DirEntry->FileName, NULL, FALSE);
1225 }
1226 Status = DirIterClose(&DirIter);
1227 if (Status != EFI_NOT_FOUND) {
1228 SPrint(FileName, 255, L"while scanning the %s directory", Path);
1229 CheckError(Status, FileName);
1230 }
1231 return (NumFound);
1232 }
1233
1234 static EFI_STATUS ConnectAllDriversToAllControllers(VOID)
1235 {
1236 EFI_STATUS Status;
1237 UINTN AllHandleCount;
1238 EFI_HANDLE *AllHandleBuffer;
1239 UINTN Index;
1240 UINTN HandleCount;
1241 EFI_HANDLE *HandleBuffer;
1242 UINT32 *HandleType;
1243 UINTN HandleIndex;
1244 BOOLEAN Parent;
1245 BOOLEAN Device;
1246
1247 Status = LibLocateHandle(AllHandles,
1248 NULL,
1249 NULL,
1250 &AllHandleCount,
1251 &AllHandleBuffer);
1252 if (EFI_ERROR(Status))
1253 return Status;
1254
1255 for (Index = 0; Index < AllHandleCount; Index++) {
1256 //
1257 // Scan the handle database
1258 //
1259 Status = LibScanHandleDatabase(NULL,
1260 NULL,
1261 AllHandleBuffer[Index],
1262 NULL,
1263 &HandleCount,
1264 &HandleBuffer,
1265 &HandleType);
1266 if (EFI_ERROR (Status))
1267 goto Done;
1268
1269 Device = TRUE;
1270 if (HandleType[Index] & EFI_HANDLE_TYPE_DRIVER_BINDING_HANDLE)
1271 Device = FALSE;
1272 if (HandleType[Index] & EFI_HANDLE_TYPE_IMAGE_HANDLE)
1273 Device = FALSE;
1274
1275 if (Device) {
1276 Parent = FALSE;
1277 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
1278 if (HandleType[HandleIndex] & EFI_HANDLE_TYPE_PARENT_HANDLE)
1279 Parent = TRUE;
1280 } // for
1281
1282 if (!Parent) {
1283 if (HandleType[Index] & EFI_HANDLE_TYPE_DEVICE_HANDLE) {
1284 Status = refit_call4_wrapper(BS->ConnectController,
1285 AllHandleBuffer[Index],
1286 NULL,
1287 NULL,
1288 TRUE);
1289 }
1290 }
1291 }
1292
1293 FreePool (HandleBuffer);
1294 FreePool (HandleType);
1295 }
1296
1297 Done:
1298 FreePool (AllHandleBuffer);
1299 return Status;
1300 } /* EFI_STATUS ConnectAllDriversToAllControllers() */
1301
1302 // Load all EFI drivers from rEFInd's "drivers" subdirectory and from the
1303 // directories specified by the user in the "scan_driver_dirs" configuration
1304 // file line.
1305 static VOID LoadDrivers(VOID)
1306 {
1307 CHAR16 *Directory;
1308 UINTN i = 0, Length, NumFound = 0;
1309
1310 // load drivers from the "drivers" subdirectory of rEFInd's home directory
1311 Directory = StrDuplicate(SelfDirPath);
1312 CleanUpPathNameSlashes(Directory);
1313 MergeStrings(&Directory, L"drivers", L'\\');
1314 NumFound += ScanDriverDir(Directory);
1315
1316 // Scan additional user-specified driver directories....
1317 while ((Directory = FindCommaDelimited(GlobalConfig.DriverDirs, i++)) != NULL) {
1318 CleanUpPathNameSlashes(Directory);
1319 Length = StrLen(Directory);
1320 if (Length > 0)
1321 NumFound += ScanDriverDir(Directory);
1322 FreePool(Directory);
1323 } // while
1324
1325 // connect all devices
1326 if (NumFound > 0)
1327 ConnectAllDriversToAllControllers();
1328 } /* static VOID LoadDrivers() */
1329
1330 static VOID ScanForBootloaders(VOID) {
1331 UINTN i;
1332
1333 ScanVolumes();
1334 // Commented-out below: Was part of an attempt to get rEFInd to
1335 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1336 // removed....
1337 // MainMenu.Title = StrDuplicate(L"Main Menu 2");
1338 // MainMenu.TitleImage = NULL;
1339 // MainMenu.InfoLineCount = 0;
1340 // MainMenu.InfoLines = NULL;
1341 // MainMenu.EntryCount = 0;
1342 // MainMenu.Entries = NULL;
1343 // MainMenu.TimeoutSeconds = 20;
1344 // MainMenu.TimeoutText = StrDuplicate(L"Automatic boot");
1345 // DebugPause();
1346
1347 // scan for loaders and tools, add them to the menu
1348 for (i = 0; i < NUM_SCAN_OPTIONS; i++) {
1349 switch(GlobalConfig.ScanFor[i]) {
1350 case 'c': case 'C':
1351 ScanLegacyDisc();
1352 break;
1353 case 'h': case 'H':
1354 ScanLegacyInternal();
1355 break;
1356 case 'b': case 'B':
1357 ScanLegacyExternal();
1358 break;
1359 case 'm': case 'M':
1360 ScanUserConfigured();
1361 break;
1362 case 'e': case 'E':
1363 ScanExternal();
1364 break;
1365 case 'i': case 'I':
1366 ScanInternal();
1367 break;
1368 case 'o': case 'O':
1369 ScanOptical();
1370 break;
1371 } // switch()
1372 } // for
1373
1374 // assign shortcut keys
1375 for (i = 0; i < MainMenu.EntryCount && MainMenu.Entries[i]->Row == 0 && i < 9; i++)
1376 MainMenu.Entries[i]->ShortcutDigit = (CHAR16)('1' + i);
1377
1378 // wait for user ACK when there were errors
1379 FinishTextScreen(FALSE);
1380 } // static VOID ScanForBootloaders()
1381
1382 // Add the second-row tags containing built-in and external tools (EFI shell,
1383 // reboot, etc.)
1384 static VOID ScanForTools(VOID) {
1385 CHAR16 *FileName = NULL;
1386 UINTN i, j;
1387
1388 for (i = 0; i < NUM_TOOLS; i++) {
1389 switch(GlobalConfig.ShowTools[i]) {
1390 case TAG_SHUTDOWN:
1391 MenuEntryShutdown.Image = BuiltinIcon(BUILTIN_ICON_FUNC_SHUTDOWN);
1392 AddMenuEntry(&MainMenu, &MenuEntryShutdown);
1393 break;
1394 case TAG_REBOOT:
1395 MenuEntryReset.Image = BuiltinIcon(BUILTIN_ICON_FUNC_RESET);
1396 AddMenuEntry(&MainMenu, &MenuEntryReset);
1397 break;
1398 case TAG_ABOUT:
1399 MenuEntryAbout.Image = BuiltinIcon(BUILTIN_ICON_FUNC_ABOUT);
1400 AddMenuEntry(&MainMenu, &MenuEntryAbout);
1401 break;
1402 case TAG_EXIT:
1403 MenuEntryExit.Image = BuiltinIcon(BUILTIN_ICON_FUNC_EXIT);
1404 AddMenuEntry(&MainMenu, &MenuEntryExit);
1405 break;
1406 case TAG_SHELL:
1407 j = 0;
1408 while ((FileName = FindCommaDelimited(SHELL_NAMES, j++)) != NULL) {
1409 if (FileExists(SelfRootDir, FileName)) {
1410 AddToolEntry(FileName, L"EFI Shell", BuiltinIcon(BUILTIN_ICON_TOOL_SHELL), 'E', FALSE);
1411 }
1412 } // while
1413 break;
1414 case TAG_GPTSYNC:
1415 MergeStrings(&FileName, L"\\efi\\tools\\gptsync.efi", 0);
1416 if (FileExists(SelfRootDir, FileName)) {
1417 AddToolEntry(FileName, L"Make Hybrid MBR", BuiltinIcon(BUILTIN_ICON_TOOL_PART), 'P', FALSE);
1418 }
1419 break;
1420 } // switch()
1421 if (FileName != NULL) {
1422 FreePool(FileName);
1423 FileName = NULL;
1424 }
1425 } // for
1426 } // static VOID ScanForTools
1427
1428 //
1429 // main entry point
1430 //
1431
1432 EFI_STATUS
1433 EFIAPI
1434 efi_main (IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
1435 {
1436 EFI_STATUS Status;
1437 BOOLEAN MainLoopRunning = TRUE;
1438 REFIT_MENU_ENTRY *ChosenEntry;
1439 UINTN MenuExit;
1440
1441 // bootstrap
1442 InitializeLib(ImageHandle, SystemTable);
1443 InitScreen();
1444 Status = InitRefitLib(ImageHandle);
1445 if (EFI_ERROR(Status))
1446 return Status;
1447
1448 // read configuration
1449 CopyMem(GlobalConfig.ScanFor, "ieo ", NUM_SCAN_OPTIONS);
1450 ReadConfig();
1451 MainMenu.TimeoutSeconds = GlobalConfig.Timeout;
1452
1453 // disable EFI watchdog timer
1454 refit_call4_wrapper(BS->SetWatchdogTimer, 0x0000, 0x0000, 0x0000, NULL);
1455
1456 // further bootstrap (now with config available)
1457 SetupScreen();
1458 LoadDrivers();
1459 ScanForBootloaders();
1460 ScanForTools();
1461
1462 while (MainLoopRunning) {
1463 MenuExit = RunMainMenu(&MainMenu, GlobalConfig.DefaultSelection, &ChosenEntry);
1464
1465 // We don't allow exiting the main menu with the Escape key.
1466 if (MenuExit == MENU_EXIT_ESCAPE) {
1467 // Commented-out below: Was part of an attempt to get rEFInd to
1468 // re-scan disk devices on pressing Esc; but doesn't work (yet), so
1469 // removed....
1470 // ReadConfig();
1471 // ScanForBootloaders();
1472 // SetupScreen();
1473 continue;
1474 }
1475
1476 switch (ChosenEntry->Tag) {
1477
1478 case TAG_REBOOT: // Reboot
1479 TerminateScreen();
1480 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1481 MainLoopRunning = FALSE; // just in case we get this far
1482 break;
1483
1484 case TAG_SHUTDOWN: // Shut Down
1485 TerminateScreen();
1486 refit_call4_wrapper(RT->ResetSystem, EfiResetShutdown, EFI_SUCCESS, 0, NULL);
1487 MainLoopRunning = FALSE; // just in case we get this far
1488 break;
1489
1490 case TAG_ABOUT: // About rEFInd
1491 AboutrEFInd();
1492 break;
1493
1494 case TAG_LOADER: // Boot OS via .EFI loader
1495 StartLoader((LOADER_ENTRY *)ChosenEntry);
1496 break;
1497
1498 case TAG_LEGACY: // Boot legacy OS
1499 StartLegacy((LEGACY_ENTRY *)ChosenEntry);
1500 break;
1501
1502 case TAG_TOOL: // Start a EFI tool
1503 StartTool((LOADER_ENTRY *)ChosenEntry);
1504 break;
1505
1506 case TAG_EXIT: // Terminate rEFInd
1507 BeginTextScreen(L" ");
1508 return EFI_SUCCESS;
1509 break;
1510
1511 }
1512 }
1513
1514 // If we end up here, things have gone wrong. Try to reboot, and if that
1515 // fails, go into an endless loop.
1516 refit_call4_wrapper(RT->ResetSystem, EfiResetCold, EFI_SUCCESS, 0, NULL);
1517 EndlessIdleLoop();
1518
1519 return EFI_SUCCESS;
1520 } /* efi_main() */