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