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