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