]> code.delx.au - refind/blob - refind/legacy.c
Moved legacy functions from refind/main.c to their own file.
[refind] / refind / legacy.c
1 /*
2 * refind/legacy.c
3 * Functions related to BIOS/CSM/legacy booting
4 *
5 * Copyright (c) 2006 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-2015 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 "icns.h"
47 #include "legacy.h"
48 #include "lib.h"
49 #include "menu.h"
50 #include "refit_call_wrapper.h"
51 #include "screen.h"
52 #include "syslinux_mbr.h"
53 #include "../EfiLib/BdsHelper.h"
54 #include "../EfiLib/legacy.h"
55
56 extern REFIT_MENU_ENTRY MenuEntryReturn;
57 extern REFIT_MENU_SCREEN MainMenu;
58
59 static EFI_STATUS ActivateMbrPartition(IN EFI_BLOCK_IO *BlockIO, IN UINTN PartitionIndex)
60 {
61 EFI_STATUS Status;
62 UINT8 SectorBuffer[512];
63 MBR_PARTITION_INFO *MbrTable, *EMbrTable;
64 UINT32 ExtBase, ExtCurrent, NextExtCurrent;
65 UINTN LogicalPartitionIndex = 4;
66 UINTN i;
67 BOOLEAN HaveBootCode;
68
69 // read MBR
70 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
71 if (EFI_ERROR(Status))
72 return Status;
73 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
74 return EFI_NOT_FOUND; // safety measure #1
75
76 // add boot code if necessary
77 HaveBootCode = FALSE;
78 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
79 if (SectorBuffer[i] != 0) {
80 HaveBootCode = TRUE;
81 break;
82 }
83 }
84 if (!HaveBootCode) {
85 // no boot code found in the MBR, add the syslinux MBR code
86 SetMem(SectorBuffer, MBR_BOOTCODE_SIZE, 0);
87 CopyMem(SectorBuffer, syslinux_mbr, SYSLINUX_MBR_SIZE);
88 }
89
90 // set the partition active
91 MbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
92 ExtBase = 0;
93 for (i = 0; i < 4; i++) {
94 if (MbrTable[i].Flags != 0x00 && MbrTable[i].Flags != 0x80)
95 return EFI_NOT_FOUND; // safety measure #2
96 if (i == PartitionIndex)
97 MbrTable[i].Flags = 0x80;
98 else if (PartitionIndex >= 4 && IS_EXTENDED_PART_TYPE(MbrTable[i].Type)) {
99 MbrTable[i].Flags = 0x80;
100 ExtBase = MbrTable[i].StartLBA;
101 } else
102 MbrTable[i].Flags = 0x00;
103 }
104
105 // write MBR
106 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, 0, 512, SectorBuffer);
107 if (EFI_ERROR(Status))
108 return Status;
109
110 if (PartitionIndex >= 4) {
111 // we have to activate a logical partition, so walk the EMBR chain
112
113 // NOTE: ExtBase was set above while looking at the MBR table
114 for (ExtCurrent = ExtBase; ExtCurrent; ExtCurrent = NextExtCurrent) {
115 // read current EMBR
116 Status = refit_call5_wrapper(BlockIO->ReadBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
117 if (EFI_ERROR(Status))
118 return Status;
119 if (*((UINT16 *)(SectorBuffer + 510)) != 0xaa55)
120 return EFI_NOT_FOUND; // safety measure #3
121
122 // scan EMBR, set appropriate partition active
123 EMbrTable = (MBR_PARTITION_INFO *)(SectorBuffer + 446);
124 NextExtCurrent = 0;
125 for (i = 0; i < 4; i++) {
126 if (EMbrTable[i].Flags != 0x00 && EMbrTable[i].Flags != 0x80)
127 return EFI_NOT_FOUND; // safety measure #4
128 if (EMbrTable[i].StartLBA == 0 || EMbrTable[i].Size == 0)
129 break;
130 if (IS_EXTENDED_PART_TYPE(EMbrTable[i].Type)) {
131 // link to next EMBR
132 NextExtCurrent = ExtBase + EMbrTable[i].StartLBA;
133 EMbrTable[i].Flags = (PartitionIndex >= LogicalPartitionIndex) ? 0x80 : 0x00;
134 break;
135 } else {
136 // logical partition
137 EMbrTable[i].Flags = (PartitionIndex == LogicalPartitionIndex) ? 0x80 : 0x00;
138 LogicalPartitionIndex++;
139 }
140 }
141
142 // write current EMBR
143 Status = refit_call5_wrapper(BlockIO->WriteBlocks, BlockIO, BlockIO->Media->MediaId, ExtCurrent, 512, SectorBuffer);
144 if (EFI_ERROR(Status))
145 return Status;
146
147 if (PartitionIndex < LogicalPartitionIndex)
148 break; // stop the loop, no need to touch further EMBRs
149 }
150
151 }
152
153 return EFI_SUCCESS;
154 } /* static EFI_STATUS ActivateMbrPartition() */
155
156 static EFI_GUID AppleVariableVendorID = { 0x7C436110, 0xAB2A, 0x4BBB, 0xA8, 0x80, 0xFE, 0x41, 0x99, 0x5C, 0x9F, 0x82 };
157
158 static EFI_STATUS WriteBootDiskHint(IN EFI_DEVICE_PATH *WholeDiskDevicePath)
159 {
160 EFI_STATUS Status;
161
162 Status = refit_call5_wrapper(RT->SetVariable, L"BootCampHD", &AppleVariableVendorID,
163 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
164 GetDevicePathSize(WholeDiskDevicePath), WholeDiskDevicePath);
165 if (EFI_ERROR(Status))
166 return Status;
167
168 return EFI_SUCCESS;
169 }
170
171 // early 2006 Core Duo / Core Solo models
172 static UINT8 LegacyLoaderDevicePath1Data[] = {
173 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
174 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
175 0xFF, 0xFF, 0xF9, 0xFF, 0x00, 0x00, 0x00, 0x00,
176 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
177 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
178 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
179 };
180 // mid-2006 Mac Pro (and probably other Core 2 models)
181 static UINT8 LegacyLoaderDevicePath2Data[] = {
182 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
183 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
184 0xFF, 0xFF, 0xF7, 0xFF, 0x00, 0x00, 0x00, 0x00,
185 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
186 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
187 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
188 };
189 // mid-2007 MBP ("Santa Rosa" based models)
190 static UINT8 LegacyLoaderDevicePath3Data[] = {
191 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
192 0x00, 0x00, 0xE0, 0xFF, 0x00, 0x00, 0x00, 0x00,
193 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
194 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
195 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
196 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
197 };
198 // early-2008 MBA
199 static UINT8 LegacyLoaderDevicePath4Data[] = {
200 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
201 0x00, 0x00, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00,
202 0xFF, 0xFF, 0xF8, 0xFF, 0x00, 0x00, 0x00, 0x00,
203 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
204 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
205 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
206 };
207 // late-2008 MB/MBP (NVidia chipset)
208 static UINT8 LegacyLoaderDevicePath5Data[] = {
209 0x01, 0x03, 0x18, 0x00, 0x0B, 0x00, 0x00, 0x00,
210 0x00, 0x40, 0xCB, 0xFF, 0x00, 0x00, 0x00, 0x00,
211 0xFF, 0xBF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
212 0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
213 0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
214 0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00,
215 };
216
217 static EFI_DEVICE_PATH *LegacyLoaderList[] = {
218 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath1Data,
219 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath2Data,
220 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath3Data,
221 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath4Data,
222 (EFI_DEVICE_PATH *)LegacyLoaderDevicePath5Data,
223 NULL
224 };
225
226 #define MAX_DISCOVERED_PATHS (16)
227
228 VOID StartLegacy(IN LEGACY_ENTRY *Entry, IN CHAR16 *SelectionName)
229 {
230 EFI_STATUS Status;
231 EG_IMAGE *BootLogoImage;
232 UINTN ErrorInStep = 0;
233 EFI_DEVICE_PATH *DiscoveredPathList[MAX_DISCOVERED_PATHS];
234
235 BeginExternalScreen(TRUE, L"Booting Legacy OS (Mac mode)");
236
237 BootLogoImage = LoadOSIcon(Entry->Volume->OSIconName, L"legacy", TRUE);
238 if (BootLogoImage != NULL)
239 BltImageAlpha(BootLogoImage,
240 (UGAWidth - BootLogoImage->Width ) >> 1,
241 (UGAHeight - BootLogoImage->Height) >> 1,
242 &StdBackgroundPixel);
243
244 if (Entry->Volume->IsMbrPartition)
245 ActivateMbrPartition(Entry->Volume->WholeDiskBlockIO, Entry->Volume->MbrPartitionIndex);
246
247 if (Entry->Volume->DiskKind != DISK_KIND_OPTICAL && Entry->Volume->WholeDiskDevicePath != NULL)
248 WriteBootDiskHint(Entry->Volume->WholeDiskDevicePath);
249
250 ExtractLegacyLoaderPaths(DiscoveredPathList, MAX_DISCOVERED_PATHS, LegacyLoaderList);
251
252 StoreLoaderName(SelectionName);
253 Status = StartEFIImageList(DiscoveredPathList, Entry->LoadOptions, TYPE_LEGACY, L"legacy loader", 0, &ErrorInStep, TRUE, FALSE);
254 if (Status == EFI_NOT_FOUND) {
255 if (ErrorInStep == 1) {
256 Print(L"\nPlease make sure that you have the latest firmware update installed.\n");
257 } else if (ErrorInStep == 3) {
258 Print(L"\nThe firmware refused to boot from the selected volume. Note that external\n"
259 L"hard drives are not well-supported by Apple's firmware for legacy OS booting.\n");
260 }
261 }
262 FinishExternalScreen();
263 } /* static VOID StartLegacy() */
264
265 // Start a device on a non-Mac using the EFI_LEGACY_BIOS_PROTOCOL
266 VOID StartLegacyUEFI(LEGACY_ENTRY *Entry, CHAR16 *SelectionName)
267 {
268 BeginExternalScreen(TRUE, L"Booting Legacy OS (UEFI mode)");
269 StoreLoaderName(SelectionName);
270
271 BdsLibConnectDevicePath (Entry->BdsOption->DevicePath);
272 BdsLibDoLegacyBoot(Entry->BdsOption);
273
274 // If we get here, it means that there was a failure....
275 Print(L"Failure booting legacy (BIOS) OS.");
276 PauseForKey();
277 FinishExternalScreen();
278 } // static VOID StartLegacyUEFI()
279
280 static LEGACY_ENTRY * AddLegacyEntry(IN CHAR16 *LoaderTitle, IN REFIT_VOLUME *Volume)
281 {
282 LEGACY_ENTRY *Entry, *SubEntry;
283 REFIT_MENU_SCREEN *SubScreen;
284 CHAR16 *VolDesc, *LegacyTitle;
285 CHAR16 ShortcutLetter = 0;
286
287 if (LoaderTitle == NULL) {
288 if (Volume->OSName != NULL) {
289 LoaderTitle = Volume->OSName;
290 if (LoaderTitle[0] == 'W' || LoaderTitle[0] == 'L')
291 ShortcutLetter = LoaderTitle[0];
292 } else
293 LoaderTitle = L"Legacy OS";
294 }
295 if (Volume->VolName != NULL)
296 VolDesc = Volume->VolName;
297 else
298 VolDesc = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" : L"HD";
299
300 LegacyTitle = AllocateZeroPool(256 * sizeof(CHAR16));
301 if (LegacyTitle != NULL)
302 SPrint(LegacyTitle, 255, L"Boot %s from %s", LoaderTitle, VolDesc);
303 if (IsInSubstring(LegacyTitle, GlobalConfig.DontScanVolumes)) {
304 MyFreePool(LegacyTitle);
305 return NULL;
306 } // if
307
308 // prepare the menu entry
309 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
310 Entry->me.Title = LegacyTitle;
311 Entry->me.Tag = TAG_LEGACY;
312 Entry->me.Row = 0;
313 Entry->me.ShortcutLetter = ShortcutLetter;
314 Entry->me.Image = LoadOSIcon(Volume->OSIconName, L"legacy", FALSE);
315 Entry->me.BadgeImage = Volume->VolBadgeImage;
316 Entry->Volume = Volume;
317 Entry->LoadOptions = (Volume->DiskKind == DISK_KIND_OPTICAL) ? L"CD" :
318 ((Volume->DiskKind == DISK_KIND_EXTERNAL) ? L"USB" : L"HD");
319 Entry->Enabled = TRUE;
320
321 // create the submenu
322 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
323 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
324 SPrint(SubScreen->Title, 255, L"Boot Options for %s on %s", LoaderTitle, VolDesc);
325 SubScreen->TitleImage = Entry->me.Image;
326 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
327 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
328 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
329 } else {
330 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
331 } // if/else
332
333 // default entry
334 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
335 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
336 SPrint(SubEntry->me.Title, 255, L"Boot %s", LoaderTitle);
337 SubEntry->me.Tag = TAG_LEGACY;
338 SubEntry->Volume = Entry->Volume;
339 SubEntry->LoadOptions = Entry->LoadOptions;
340 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
341
342 AddMenuEntry(SubScreen, &MenuEntryReturn);
343 Entry->me.SubScreen = SubScreen;
344 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
345 return Entry;
346 } /* static LEGACY_ENTRY * AddLegacyEntry() */
347
348
349 /**
350 Create a rEFInd boot option from a Legacy BIOS protocol option.
351 */
352 static LEGACY_ENTRY * AddLegacyEntryUEFI(BDS_COMMON_OPTION *BdsOption, IN UINT16 DiskType)
353 {
354 LEGACY_ENTRY *Entry, *SubEntry;
355 REFIT_MENU_SCREEN *SubScreen;
356 CHAR16 ShortcutLetter = 0;
357 CHAR16 *LegacyDescription = StrDuplicate(BdsOption->Description);
358
359 if (IsInSubstring(LegacyDescription, GlobalConfig.DontScanVolumes))
360 return NULL;
361
362 // Remove stray spaces, since many EFIs produce descriptions with lots of
363 // extra spaces, especially at the end; this throws off centering of the
364 // description on the screen....
365 LimitStringLength(LegacyDescription, 100);
366
367 // prepare the menu entry
368 Entry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
369 Entry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
370 SPrint(Entry->me.Title, 255, L"Boot legacy target %s", LegacyDescription);
371 Entry->me.Tag = TAG_LEGACY_UEFI;
372 Entry->me.Row = 0;
373 Entry->me.ShortcutLetter = ShortcutLetter;
374 Entry->me.Image = LoadOSIcon(L"legacy", L"legacy", TRUE);
375 Entry->LoadOptions = (DiskType == BBS_CDROM) ? L"CD" :
376 ((DiskType == BBS_USB) ? L"USB" : L"HD");
377 Entry->me.BadgeImage = GetDiskBadge(DiskType);
378 Entry->BdsOption = BdsOption;
379 Entry->Enabled = TRUE;
380
381 // create the submenu
382 SubScreen = AllocateZeroPool(sizeof(REFIT_MENU_SCREEN));
383 SubScreen->Title = AllocateZeroPool(256 * sizeof(CHAR16));
384 SPrint(SubScreen->Title, 255, L"No boot options for legacy target");
385 SubScreen->TitleImage = Entry->me.Image;
386 SubScreen->Hint1 = StrDuplicate(SUBSCREEN_HINT1);
387 if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_EDITOR) {
388 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2_NO_EDITOR);
389 } else {
390 SubScreen->Hint2 = StrDuplicate(SUBSCREEN_HINT2);
391 } // if/else
392
393 // default entry
394 SubEntry = AllocateZeroPool(sizeof(LEGACY_ENTRY));
395 SubEntry->me.Title = AllocateZeroPool(256 * sizeof(CHAR16));
396 SPrint(SubEntry->me.Title, 255, L"Boot %s", LegacyDescription);
397 SubEntry->me.Tag = TAG_LEGACY_UEFI;
398 Entry->BdsOption = BdsOption;
399 AddMenuEntry(SubScreen, (REFIT_MENU_ENTRY *)SubEntry);
400
401 AddMenuEntry(SubScreen, &MenuEntryReturn);
402 Entry->me.SubScreen = SubScreen;
403 AddMenuEntry(&MainMenu, (REFIT_MENU_ENTRY *)Entry);
404 return Entry;
405 } /* static LEGACY_ENTRY * AddLegacyEntryUEFI() */
406
407 /**
408 Scan for legacy BIOS targets on machines that implement EFI_LEGACY_BIOS_PROTOCOL.
409 In testing, protocol has not been implemented on Macs but has been
410 implemented on most UEFI PCs.
411 Restricts output to disks of the specified DiskType.
412 */
413 static VOID ScanLegacyUEFI(IN UINTN DiskType)
414 {
415 EFI_STATUS Status;
416 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
417 UINT16 *BootOrder = NULL;
418 UINTN Index = 0;
419 CHAR16 BootOption[10];
420 UINTN BootOrderSize = 0;
421 CHAR16 Buffer[20];
422 BDS_COMMON_OPTION *BdsOption;
423 LIST_ENTRY TempList;
424 BBS_BBS_DEVICE_PATH *BbsDevicePath = NULL;
425 BOOLEAN SearchingForUsb = FALSE;
426
427 InitializeListHead (&TempList);
428 ZeroMem (Buffer, sizeof (Buffer));
429
430 // If LegacyBios protocol is not implemented on this platform, then
431 //we do not support this type of legacy boot on this machine.
432 Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
433 if (EFI_ERROR (Status))
434 return;
435
436 // EFI calls USB drives BBS_HARDDRIVE, but we want to distinguish them,
437 // so we set DiskType inappropriately elsewhere in the program and
438 // "translate" it here.
439 if (DiskType == BBS_USB) {
440 DiskType = BBS_HARDDISK;
441 SearchingForUsb = TRUE;
442 } // if
443
444 // Grab the boot order
445 BootOrder = BdsLibGetVariableAndSize(L"BootOrder", &gEfiGlobalVariableGuid, &BootOrderSize);
446 if (BootOrder == NULL) {
447 BootOrderSize = 0;
448 }
449
450 Index = 0;
451 while (Index < BootOrderSize / sizeof (UINT16))
452 {
453 // Grab each boot option variable from the boot order, and convert
454 // the variable into a BDS boot option
455 UnicodeSPrint (BootOption, sizeof (BootOption), L"Boot%04x", BootOrder[Index]);
456 BdsOption = BdsLibVariableToOption (&TempList, BootOption);
457
458 if (BdsOption != NULL) {
459 BbsDevicePath = (BBS_BBS_DEVICE_PATH *)BdsOption->DevicePath;
460 // Only add the entry if it is of a requested type (e.g. USB, HD)
461 // Two checks necessary because some systems return EFI boot loaders
462 // with a DeviceType value that would inappropriately include them
463 // as legacy loaders....
464 if ((BbsDevicePath->DeviceType == DiskType) && (BdsOption->DevicePath->Type == DEVICE_TYPE_BIOS)) {
465 // USB flash drives appear as hard disks with certain media flags set.
466 // Look for this, and if present, pass it on with the (technically
467 // incorrect, but internally useful) BBS_TYPE_USB flag set.
468 if (DiskType == BBS_HARDDISK) {
469 if (SearchingForUsb && (BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) {
470 AddLegacyEntryUEFI(BdsOption, BBS_USB);
471 } else if (!SearchingForUsb && !(BbsDevicePath->StatusFlag & (BBS_MEDIA_PRESENT | BBS_MEDIA_MAYBE_PRESENT))) {
472 AddLegacyEntryUEFI(BdsOption, DiskType);
473 }
474 } else {
475 AddLegacyEntryUEFI(BdsOption, DiskType);
476 } // if/else
477 } // if
478 } // if (BdsOption != NULL)
479 Index++;
480 } // while
481 } /* static VOID ScanLegacyUEFI() */
482
483 static VOID ScanLegacyVolume(REFIT_VOLUME *Volume, UINTN VolumeIndex) {
484 UINTN VolumeIndex2;
485 BOOLEAN ShowVolume, HideIfOthersFound;
486
487 ShowVolume = FALSE;
488 HideIfOthersFound = FALSE;
489 if (Volume->IsAppleLegacy) {
490 ShowVolume = TRUE;
491 HideIfOthersFound = TRUE;
492 } else if (Volume->HasBootCode) {
493 ShowVolume = TRUE;
494 if (Volume->BlockIO == Volume->WholeDiskBlockIO &&
495 Volume->BlockIOOffset == 0 &&
496 Volume->OSName == NULL)
497 // this is a whole disk (MBR) entry; hide if we have entries for partitions
498 HideIfOthersFound = TRUE;
499 }
500 if (HideIfOthersFound) {
501 // check for other bootable entries on the same disk
502 for (VolumeIndex2 = 0; VolumeIndex2 < VolumesCount; VolumeIndex2++) {
503 if (VolumeIndex2 != VolumeIndex && Volumes[VolumeIndex2]->HasBootCode &&
504 Volumes[VolumeIndex2]->WholeDiskBlockIO == Volume->WholeDiskBlockIO)
505 ShowVolume = FALSE;
506 }
507 }
508
509 if (ShowVolume)
510 AddLegacyEntry(NULL, Volume);
511 } // static VOID ScanLegacyVolume()
512
513 // Scan attached optical discs for legacy (BIOS) boot code
514 // and add anything found to the list....
515 VOID ScanLegacyDisc(VOID)
516 {
517 UINTN VolumeIndex;
518 REFIT_VOLUME *Volume;
519
520 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
521 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
522 Volume = Volumes[VolumeIndex];
523 if (Volume->DiskKind == DISK_KIND_OPTICAL)
524 ScanLegacyVolume(Volume, VolumeIndex);
525 } // for
526 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
527 ScanLegacyUEFI(BBS_CDROM);
528 }
529 } /* VOID ScanLegacyDisc() */
530
531 // Scan internal hard disks for legacy (BIOS) boot code
532 // and add anything found to the list....
533 VOID ScanLegacyInternal(VOID)
534 {
535 UINTN VolumeIndex;
536 REFIT_VOLUME *Volume;
537
538 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
539 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
540 Volume = Volumes[VolumeIndex];
541 if (Volume->DiskKind == DISK_KIND_INTERNAL)
542 ScanLegacyVolume(Volume, VolumeIndex);
543 } // for
544 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
545 // TODO: This actually picks up USB flash drives, too; try to find
546 // a way to differentiate the two....
547 ScanLegacyUEFI(BBS_HARDDISK);
548 }
549 } /* VOID ScanLegacyInternal() */
550
551 // Scan external disks for legacy (BIOS) boot code
552 // and add anything found to the list....
553 VOID ScanLegacyExternal(VOID)
554 {
555 UINTN VolumeIndex;
556 REFIT_VOLUME *Volume;
557
558 if (GlobalConfig.LegacyType == LEGACY_TYPE_MAC) {
559 for (VolumeIndex = 0; VolumeIndex < VolumesCount; VolumeIndex++) {
560 Volume = Volumes[VolumeIndex];
561 if (Volume->DiskKind == DISK_KIND_EXTERNAL)
562 ScanLegacyVolume(Volume, VolumeIndex);
563 } // for
564 } else if (GlobalConfig.LegacyType == LEGACY_TYPE_UEFI) {
565 // TODO: This actually doesn't do anything useful; leaving in hopes of
566 // fixing it later....
567 ScanLegacyUEFI(BBS_USB);
568 }
569 } /* VOID ScanLegacyExternal() */
570
571 // Determine what (if any) type of legacy (BIOS) boot support is available
572 VOID FindLegacyBootType(VOID) {
573 EFI_STATUS Status;
574 EFI_LEGACY_BIOS_PROTOCOL *LegacyBios;
575
576 GlobalConfig.LegacyType = LEGACY_TYPE_NONE;
577
578 // UEFI-style legacy BIOS support is available only with some EFI implementations....
579 Status = refit_call3_wrapper(gBS->LocateProtocol, &gEfiLegacyBootProtocolGuid, NULL, (VOID **) &LegacyBios);
580 if (!EFI_ERROR (Status))
581 GlobalConfig.LegacyType = LEGACY_TYPE_UEFI;
582
583 // Macs have their own system. If the firmware vendor code contains the
584 // string "Apple", assume it's available. Note that this overrides the
585 // UEFI type, and might yield false positives if the vendor string
586 // contains "Apple" as part of something bigger, so this isn't 100%
587 // perfect.
588 if (StriSubCmp(L"Apple", ST->FirmwareVendor))
589 GlobalConfig.LegacyType = LEGACY_TYPE_MAC;
590 } // VOID FindLegacyBootType
591
592 // Warn the user if legacy OS scans are enabled but the firmware can't support them....
593 VOID WarnIfLegacyProblems(VOID) {
594 BOOLEAN found = FALSE;
595 UINTN i = 0;
596
597 if (GlobalConfig.LegacyType == LEGACY_TYPE_NONE) {
598 do {
599 if (GlobalConfig.ScanFor[i] == 'h' || GlobalConfig.ScanFor[i] == 'b' || GlobalConfig.ScanFor[i] == 'c' ||
600 GlobalConfig.ScanFor[i] == 'H' || GlobalConfig.ScanFor[i] == 'B' || GlobalConfig.ScanFor[i] == 'C')
601 found = TRUE;
602 i++;
603 } while ((i < NUM_SCAN_OPTIONS) && (!found));
604
605 if (found) {
606 Print(L"NOTE: refind.conf's 'scanfor' line specifies scanning for one or more legacy\n");
607 Print(L"(BIOS) boot options; however, this is not possible because your computer lacks\n");
608 Print(L"the necessary Compatibility Support Module (CSM) support or that support is\n");
609 Print(L"disabled in your firmware.\n");
610 PauseForKey();
611 } // if (found)
612 } // if no legacy support
613 } // VOID WarnIfLegacyProblems()
614