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