mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
503 lines
13 KiB
C
503 lines
13 KiB
C
/** @file
|
|
Copyright (C) 2023, Goldfish64. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include "LegacyBootInternal.h"
|
|
|
|
THUNK_CONTEXT mThunkContext;
|
|
|
|
//
|
|
// PIWG firmware media device path for Apple legacy interface.
|
|
// FwFile(2B0585EB-D8B8-49A9-8B8CE21B01AEF2B7)
|
|
//
|
|
STATIC CONST UINT8 AppleLegacyInterfaceMediaDevicePathData[] = {
|
|
0x04, 0x06, 0x14, 0x00, 0xEB, 0x85, 0x05, 0x2B,
|
|
0xB8, 0xD8, 0xA9, 0x49, 0x8B, 0x8C, 0xE2, 0x1B,
|
|
0x01, 0xAE, 0xF2, 0xB7, 0x7F, 0xFF, 0x04, 0x00
|
|
};
|
|
STATIC CONST EFI_DEVICE_PATH_PROTOCOL *AppleLegacyInterfaceMediaDevicePathPath = (EFI_DEVICE_PATH_PROTOCOL *)AppleLegacyInterfaceMediaDevicePathData;
|
|
|
|
#define MAX_APPLE_LEGACY_DEVICE_PATHS 16
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
CheckLegacySignature (
|
|
IN CONST CHAR8 *SignatureStr,
|
|
IN UINT8 *Buffer,
|
|
IN UINTN BufferSize
|
|
)
|
|
{
|
|
UINT32 Offset;
|
|
|
|
Offset = 0;
|
|
|
|
return FindPattern (
|
|
(CONST UINT8 *)SignatureStr,
|
|
NULL,
|
|
(CONST UINT32)AsciiStrLen (SignatureStr),
|
|
Buffer,
|
|
(UINT32)BufferSize,
|
|
&Offset
|
|
);
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
ScanAppleLegacyInterfacePaths (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePaths,
|
|
IN UINTN MaxDevicePaths
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN NoHandles;
|
|
EFI_HANDLE *Handles;
|
|
UINTN HandleIndex;
|
|
UINTN PathCount;
|
|
UINTN PathIndex;
|
|
BOOLEAN DevicePathExists;
|
|
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
|
|
MaxDevicePaths--;
|
|
PathCount = 0;
|
|
|
|
//
|
|
// Get all LoadedImage protocol handles.
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
NULL,
|
|
&NoHandles,
|
|
&Handles
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
for (HandleIndex = 0; HandleIndex < NoHandles && PathCount < MaxDevicePaths; HandleIndex++) {
|
|
Status = gBS->HandleProtocol (
|
|
Handles[HandleIndex],
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **)&LoadedImage
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
LoadedImage->DeviceHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID **)&DevicePath
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Legacy boot interface will be behind a memory range node.
|
|
//
|
|
if ( (DevicePathType (DevicePath) != HARDWARE_DEVICE_PATH)
|
|
|| (DevicePathSubType (DevicePath) != HW_MEMMAP_DP))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Ensure we don't add a duplicate path.
|
|
//
|
|
DevicePathExists = FALSE;
|
|
for (PathIndex = 0; PathIndex < PathCount; PathIndex++) {
|
|
if (DevicePathNodeLength (DevicePath) != DevicePathNodeLength (DevicePaths[PathIndex])) {
|
|
continue;
|
|
}
|
|
|
|
if (CompareMem (DevicePath, DevicePaths[PathIndex], DevicePathNodeLength (DevicePath))) {
|
|
DevicePathExists = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DevicePathExists) {
|
|
continue;
|
|
}
|
|
|
|
DevicePaths[PathCount++] = AppendDevicePath (DevicePath, AppleLegacyInterfaceMediaDevicePathPath);
|
|
}
|
|
|
|
FreePool (Handles);
|
|
|
|
DevicePaths[PathCount] = NULL;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalIsLegacyInterfaceSupported (
|
|
OUT BOOLEAN *IsAppleInterfaceSupported
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_LEGACY_8259_PROTOCOL *Legacy8259;
|
|
|
|
ASSERT (IsAppleInterfaceSupported != NULL);
|
|
|
|
//
|
|
// Apple legacy boot interface is only available on Apple platforms.
|
|
// Use legacy 16-bit thunks on legacy PC platforms.
|
|
//
|
|
if (OcStriCmp (L"Apple", gST->FirmwareVendor) == 0) {
|
|
*IsAppleInterfaceSupported = TRUE;
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **)&Legacy8259);
|
|
if (!EFI_ERROR (Status) && (Legacy8259 != NULL)) {
|
|
*IsAppleInterfaceSupported = FALSE;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalSetBootCampHDPath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *HdDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE DiskHandle;
|
|
EFI_HANDLE PartitionHandle;
|
|
UINT8 PartitionIndex;
|
|
EFI_DEVICE_PATH_PROTOCOL *WholeDiskPath;
|
|
|
|
WholeDiskPath = OcDiskGetDevicePath (HdDevicePath);
|
|
if (WholeDiskPath == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
DebugPrintDevicePath (DEBUG_INFO, "OLB: Legacy disk device path", WholeDiskPath);
|
|
|
|
//
|
|
// Mark target partition as active.
|
|
//
|
|
DiskHandle = OcPartitionGetDiskHandle (HdDevicePath);
|
|
if (DiskHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PartitionHandle = OcPartitionGetPartitionHandle (HdDevicePath);
|
|
if (PartitionHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = OcDiskGetMbrPartitionIndex (PartitionHandle, &PartitionIndex);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = OcDiskMarkMbrPartitionActive (DiskHandle, PartitionIndex);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Set BootCampHD variable pointing to target disk.
|
|
//
|
|
return gRT->SetVariable (
|
|
APPLE_BOOT_CAMP_HD_VARIABLE_NAME,
|
|
&gAppleBootVariableGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
GetDevicePathSize (WholeDiskPath),
|
|
WholeDiskPath
|
|
);
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalLoadAppleLegacyInterface (
|
|
IN EFI_HANDLE ParentImageHandle,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **ImageDevicePath,
|
|
OUT EFI_HANDLE *ImageHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL **LegacyDevicePaths;
|
|
CHAR16 *UnicodeDevicePath;
|
|
UINTN Index;
|
|
|
|
//
|
|
// Get list of possible locations for Apple legacy interface and attempt to load.
|
|
//
|
|
LegacyDevicePaths = AllocateZeroPool (sizeof (*LegacyDevicePaths) * MAX_APPLE_LEGACY_DEVICE_PATHS);
|
|
if (LegacyDevicePaths == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = ScanAppleLegacyInterfacePaths (LegacyDevicePaths, MAX_APPLE_LEGACY_DEVICE_PATHS);
|
|
if (!EFI_ERROR (Status)) {
|
|
for (Index = 0; LegacyDevicePaths[Index] != NULL; Index++) {
|
|
Status = gBS->LoadImage (
|
|
FALSE,
|
|
ParentImageHandle,
|
|
LegacyDevicePaths[Index],
|
|
NULL,
|
|
0,
|
|
ImageHandle
|
|
);
|
|
if (Status != EFI_NOT_FOUND) {
|
|
*ImageDevicePath = LegacyDevicePaths[Index];
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
|
|
UnicodeDevicePath = ConvertDevicePathToText (*ImageDevicePath, FALSE, FALSE);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OLB: Loaded Apple legacy interface at dp %s - %r\n",
|
|
UnicodeDevicePath != NULL ? UnicodeDevicePath : L"<null>",
|
|
Status
|
|
));
|
|
if (UnicodeDevicePath != NULL) {
|
|
FreePool (UnicodeDevicePath);
|
|
}
|
|
|
|
DEBUG_CODE_END ();
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (LegacyDevicePaths);
|
|
|
|
return Status;
|
|
}
|
|
|
|
OC_LEGACY_OS_TYPE
|
|
InternalGetPartitionLegacyOsType (
|
|
IN EFI_HANDLE PartitionHandle,
|
|
IN BOOLEAN IsCdRomSupported
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Buffer;
|
|
UINTN BufferSize;
|
|
MASTER_BOOT_RECORD *Mbr;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_HANDLE DiskHandle;
|
|
OC_LEGACY_OS_TYPE LegacyOsType;
|
|
|
|
ASSERT (PartitionHandle != NULL);
|
|
|
|
DevicePath = DevicePathFromHandle (PartitionHandle);
|
|
if (DevicePath == NULL) {
|
|
return OcLegacyOsTypeNone;
|
|
}
|
|
|
|
DiskHandle = OcPartitionGetDiskHandle (DevicePath);
|
|
if (DiskHandle == NULL) {
|
|
return OcLegacyOsTypeNone;
|
|
}
|
|
|
|
//
|
|
// For CD devices, validate El-Torito structures.
|
|
// For hard disk and USB devices, validate MBR and PBR of target partition.
|
|
//
|
|
if (OcIsDiskCdRom (DevicePath)) {
|
|
if (!IsCdRomSupported) {
|
|
DEBUG ((DEBUG_INFO, "OLB: CD-ROM boot not supported on this platform\n"));
|
|
return OcLegacyOsTypeNone;
|
|
}
|
|
|
|
DebugPrintDevicePath (DEBUG_INFO, "OLB: Reading El-Torito for CDROM", DevicePath);
|
|
Status = OcDiskReadElTorito (DevicePath, &Buffer, &BufferSize);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Failed reading El-Torito - %r\n", Status));
|
|
return OcLegacyOsTypeNone;
|
|
}
|
|
} else {
|
|
DebugPrintDevicePath (DEBUG_INFO, "OLB: Reading MBR for parent disk", DevicePath);
|
|
Mbr = OcGetDiskMbrTable (DiskHandle, TRUE);
|
|
if (Mbr == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Disk does not contain a valid MBR\n"));
|
|
return OcLegacyOsTypeNone;
|
|
}
|
|
|
|
FreePool (Mbr);
|
|
|
|
//
|
|
// Retrieve PBR for partition.
|
|
//
|
|
DebugPrintDevicePath (DEBUG_INFO, "OLB: Reading PBR for partition", DevicePath);
|
|
Mbr = OcGetDiskMbrTable (PartitionHandle, FALSE);
|
|
if (Mbr == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Partition does not contain a valid PBR\n"));
|
|
return OcLegacyOsTypeNone;
|
|
}
|
|
|
|
Buffer = (UINT8 *)Mbr;
|
|
BufferSize = sizeof (*Mbr);
|
|
}
|
|
|
|
DebugPrintHexDump (DEBUG_INFO, "OLB: PbrHEX", Buffer, BufferSize);
|
|
|
|
//
|
|
// Validate sector contents and check for known signatures
|
|
// indicating the partition is bootable.
|
|
//
|
|
if (CheckLegacySignature ("BOOTMGR", Buffer, BufferSize)) {
|
|
LegacyOsType = OcLegacyOsTypeWindowsBootmgr;
|
|
} else if (CheckLegacySignature ("NTLDR", Buffer, BufferSize)) {
|
|
LegacyOsType = OcLegacyOsTypeWindowsNtldr;
|
|
} else if ( CheckLegacySignature ("ISOLINUX", Buffer, BufferSize)
|
|
|| CheckLegacySignature ("isolinux", Buffer, BufferSize))
|
|
{
|
|
LegacyOsType = OcLegacyOsTypeIsoLinux;
|
|
} else {
|
|
LegacyOsType = OcLegacyOsTypeNone;
|
|
DEBUG ((DEBUG_INFO, "OLB: Unknown legacy bootsector signature\n"));
|
|
}
|
|
|
|
FreePool (Buffer);
|
|
|
|
return LegacyOsType;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InternalLoadLegacyPbr (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *PartitionPath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE DiskHandle;
|
|
EFI_HANDLE PartitionHandle;
|
|
MASTER_BOOT_RECORD *Mbr;
|
|
MASTER_BOOT_RECORD *Pbr;
|
|
UINT8 PartitionIndex;
|
|
UINT8 BiosDiskAddress;
|
|
|
|
IA32_REGISTER_SET Regs;
|
|
MASTER_BOOT_RECORD *MbrPtr = (MASTER_BOOT_RECORD *)0x0600;
|
|
MASTER_BOOT_RECORD *PbrPtr = (MASTER_BOOT_RECORD *)0x7C00;
|
|
EFI_LEGACY_8259_PROTOCOL *Legacy8259;
|
|
|
|
//
|
|
// Locate Legacy8259 protocol.
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiLegacy8259ProtocolGuid,
|
|
NULL,
|
|
(VOID **)&Legacy8259
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Could not locate Legacy8259 protocol\n"));
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Retrieve the PBR from partition and partition index.
|
|
//
|
|
PartitionHandle = OcPartitionGetPartitionHandle (PartitionPath);
|
|
if (PartitionHandle == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Could not locate partition handle\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Pbr = OcGetDiskMbrTable (PartitionHandle, FALSE);
|
|
if (Pbr == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Partition does not contain a valid PBR\n"));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = OcDiskGetMbrPartitionIndex (PartitionHandle, &PartitionIndex);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (Pbr);
|
|
return Status;
|
|
}
|
|
|
|
DebugPrintHexDump (DEBUG_INFO, "OLB: PbrHEX", (UINT8 *)Pbr, sizeof (*Pbr));
|
|
|
|
//
|
|
// Retrieve MBR from disk.
|
|
//
|
|
DiskHandle = OcPartitionGetDiskHandle (PartitionPath);
|
|
if (DiskHandle == NULL) {
|
|
FreePool (Pbr);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Mbr = OcGetDiskMbrTable (DiskHandle, TRUE);
|
|
if (Mbr == NULL) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Disk does not contain a valid MBR\n"));
|
|
FreePool (Pbr);
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
DebugPrintHexDump (DEBUG_INFO, "OLB: MbrHEX", (UINT8 *)Mbr, sizeof (*Mbr));
|
|
|
|
//
|
|
// Create and initialize thunk structures.
|
|
//
|
|
Status = OcLegacyThunkInitializeBiosIntCaller (&mThunkContext);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (Pbr);
|
|
FreePool (Mbr);
|
|
return Status;
|
|
}
|
|
|
|
Status = OcLegacyThunkInitializeInterruptRedirection (Legacy8259);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (Pbr);
|
|
FreePool (Mbr);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Determine BIOS disk number.
|
|
//
|
|
Status = InternalGetBiosDiskAddress (
|
|
&mThunkContext,
|
|
Legacy8259,
|
|
DiskHandle,
|
|
&BiosDiskAddress
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "OLB: Disk address could not be determined\n"));
|
|
FreePool (Pbr);
|
|
FreePool (Mbr);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Copy MBR and PBR to low memory locations for booting.
|
|
//
|
|
CopyMem (PbrPtr, Pbr, sizeof (*Pbr));
|
|
CopyMem (MbrPtr, Mbr, sizeof (*Mbr));
|
|
|
|
DebugPrintHexDump (DEBUG_INFO, "OLB: PbrPtr", (UINT8 *)PbrPtr, sizeof (*PbrPtr));
|
|
|
|
OcLegacyThunkDisconnectEfiGraphics ();
|
|
|
|
//
|
|
// Thunk to real mode and invoke legacy boot sector.
|
|
// If successful, this function will not return.
|
|
//
|
|
ZeroMem (&Regs, sizeof (Regs));
|
|
|
|
Regs.H.DL = BiosDiskAddress;
|
|
Regs.X.SI = (UINT16)(UINTN)&MbrPtr->Partition[PartitionIndex];
|
|
OcLegacyThunkFarCall86 (
|
|
&mThunkContext,
|
|
Legacy8259,
|
|
0,
|
|
0x7c00,
|
|
&Regs,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
DEBUG ((DEBUG_WARN, "OLB: Failure calling legacy boot sector\n"));
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|