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