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