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