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