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