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