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