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