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