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