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