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