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