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