mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
310 lines
11 KiB
C
310 lines
11 KiB
C
/** @file
|
|
Copyright (C) 2019, Goldfish64. All rights reserved.
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
**/
|
|
|
|
#include "OcAppleDiskImageLibInternal.h"
|
|
|
|
// DP GUIDs.
|
|
EFI_GUID gDmgControllerDpGuid = DMG_CONTROLLER_DP_GUID;
|
|
EFI_GUID gDmgSizeDpGuid = DMG_SIZE_DP_GUID;
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FindPlistDictChild(
|
|
IN XML_NODE *Node,
|
|
IN CHAR8 *KeyName,
|
|
OUT XML_NODE **Key,
|
|
OUT XML_NODE **Value) {
|
|
// Create variables.
|
|
UINT32 ChildCount;
|
|
XML_NODE *ChildValue = NULL;
|
|
XML_NODE *ChildKey = NULL;
|
|
CONST CHAR8 *ChildKeyStr = NULL;
|
|
|
|
// Ensure parameters are valid.
|
|
if (!Node || !KeyName || !Key || !Value)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Search for key.
|
|
ChildCount = PlistDictChildren(Node);
|
|
for (UINT32 i = 0; i < ChildCount; i++) {
|
|
// Get child key/value and key name.
|
|
ChildKey = PlistDictChild(Node, i, &ChildValue);
|
|
ChildKeyStr = PlistKeyValue(ChildKey);
|
|
|
|
// Check if key matches.
|
|
if (ChildKeyStr && !AsciiStrCmp(ChildKeyStr, KeyName)) {
|
|
*Key = ChildKey;
|
|
*Value = ChildValue;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
// If we get here, we couldn't find it.
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ParsePlist(
|
|
IN VOID *Buffer,
|
|
IN UINT64 XmlOffset,
|
|
IN UINT64 XmlLength,
|
|
OUT UINT32 *BlockCount,
|
|
OUT OC_APPLE_DISK_IMAGE_BLOCK_CONTEXT **Blocks) {
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
|
|
// Plist buffer.
|
|
CHAR8 *XmlPlistBuffer = NULL;
|
|
XML_DOCUMENT *XmlPlistDoc = NULL;
|
|
XML_NODE *XmlPlistNodeRoot = NULL;
|
|
XML_NODE *XmlPlistNodeResourceForkKey = NULL;
|
|
XML_NODE *XmlPlistNodeResourceForkValue = NULL;
|
|
XML_NODE *XmlPlistNodeBlockListKey = NULL;
|
|
XML_NODE *XmlPlistNodeBlockListValue = NULL;
|
|
|
|
// Plist blocks.
|
|
XML_NODE *XmlPlistNodeBlockDict = NULL;
|
|
XML_NODE *XmlPlistBlockDictChildKey = NULL;
|
|
XML_NODE *XmlPlistBlockDictChildValue = NULL;
|
|
UINT32 XmlPlistBlockDictChildDataSize;
|
|
|
|
// DMG blocks.
|
|
UINT32 DmgBlockCount;
|
|
OC_APPLE_DISK_IMAGE_BLOCK_CONTEXT *DmgBlocks;
|
|
|
|
// Ensure parameters are valid.
|
|
if (!Buffer || !XmlLength || !BlockCount || !Blocks)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Allocate buffer for plist and copy plist to it.
|
|
XmlPlistBuffer = AllocateCopyPool(XmlLength, (UINT8*)Buffer + XmlOffset);
|
|
if (!XmlPlistBuffer) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
// Open plist and get root node.
|
|
XmlPlistDoc = XmlDocumentParse(XmlPlistBuffer, (UINT32)XmlLength, FALSE);
|
|
if (!XmlPlistDoc) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
XmlPlistNodeRoot = PlistDocumentRoot(XmlPlistDoc);
|
|
if (!XmlPlistNodeRoot) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
// Get resource fork dictionary.
|
|
Status = FindPlistDictChild(XmlPlistNodeRoot, DMG_PLIST_RESOURCE_FORK_KEY,
|
|
&XmlPlistNodeResourceForkKey, &XmlPlistNodeResourceForkValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
|
|
// Get block list dictionary.
|
|
Status = FindPlistDictChild(XmlPlistNodeResourceForkValue, DMG_PLIST_BLOCK_LIST_KEY,
|
|
&XmlPlistNodeBlockListKey, &XmlPlistNodeBlockListValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
|
|
// Get block count and allocate space for blocks.
|
|
DmgBlockCount = XmlNodeChildren(XmlPlistNodeBlockListValue);
|
|
DmgBlocks = AllocateZeroPool(DmgBlockCount * sizeof(OC_APPLE_DISK_IMAGE_BLOCK_CONTEXT));
|
|
if (!DmgBlocks) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
// Get blocks in plist.
|
|
for (UINT32 b = 0; b < DmgBlockCount; b++) {
|
|
// Get dictionary.
|
|
XmlPlistNodeBlockDict = XmlNodeChild(XmlPlistNodeBlockListValue, b);
|
|
|
|
// Get attributes. TODO they are actually string.
|
|
Status = FindPlistDictChild(XmlPlistNodeBlockDict, DMG_PLIST_ATTRIBUTES,
|
|
&XmlPlistBlockDictChildKey, &XmlPlistBlockDictChildValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
PlistIntegerValue(XmlPlistBlockDictChildValue,
|
|
&DmgBlocks[b].Attributes, sizeof(DmgBlocks[b].Attributes), FALSE);
|
|
|
|
// Get CFName node.
|
|
Status = FindPlistDictChild(XmlPlistNodeBlockDict, DMG_PLIST_CFNAME,
|
|
&XmlPlistBlockDictChildKey, &XmlPlistBlockDictChildValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
|
|
// Allocate CFName string and get it.
|
|
XmlPlistBlockDictChildDataSize = 0;
|
|
PlistStringSize(XmlPlistBlockDictChildValue, &XmlPlistBlockDictChildDataSize);
|
|
DmgBlocks[b].CfName = AllocateZeroPool(XmlPlistBlockDictChildDataSize);
|
|
if (!DmgBlocks[b].CfName) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
PlistStringValue(XmlPlistBlockDictChildValue,
|
|
DmgBlocks[b].CfName, &XmlPlistBlockDictChildDataSize);
|
|
|
|
// Get Name node.
|
|
Status = FindPlistDictChild(XmlPlistNodeBlockDict, DMG_PLIST_NAME,
|
|
&XmlPlistBlockDictChildKey, &XmlPlistBlockDictChildValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
|
|
// Allocate Name string and get it.
|
|
XmlPlistBlockDictChildDataSize = 0;
|
|
PlistStringSize(XmlPlistBlockDictChildValue, &XmlPlistBlockDictChildDataSize);
|
|
DmgBlocks[b].Name = AllocateZeroPool(XmlPlistBlockDictChildDataSize);
|
|
if (!DmgBlocks[b].Name) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
PlistStringValue(XmlPlistBlockDictChildValue,
|
|
DmgBlocks[b].Name, &XmlPlistBlockDictChildDataSize);
|
|
|
|
// Get ID.
|
|
Status = FindPlistDictChild(XmlPlistNodeBlockDict, DMG_PLIST_ID,
|
|
&XmlPlistBlockDictChildKey, &XmlPlistBlockDictChildValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
PlistIntegerValue(XmlPlistBlockDictChildValue,
|
|
&DmgBlocks[b].Id, sizeof(DmgBlocks[b].Id), FALSE);
|
|
|
|
// Get block data.
|
|
Status = FindPlistDictChild(XmlPlistNodeBlockDict, DMG_PLIST_DATA,
|
|
&XmlPlistBlockDictChildKey, &XmlPlistBlockDictChildValue);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
|
|
// Allocate block data and get it.
|
|
XmlPlistBlockDictChildDataSize = 0;
|
|
PlistDataSize(XmlPlistBlockDictChildValue, &XmlPlistBlockDictChildDataSize);
|
|
DmgBlocks[b].BlockData = AllocateZeroPool(XmlPlistBlockDictChildDataSize);
|
|
if (!DmgBlocks[b].BlockData) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
PlistDataValue(XmlPlistBlockDictChildValue,
|
|
(UINT8*)DmgBlocks[b].BlockData, &XmlPlistBlockDictChildDataSize);
|
|
|
|
// Swap block fields.
|
|
DmgBlocks[b].BlockData->Version = SwapBytes32(DmgBlocks[b].BlockData->Version);
|
|
DmgBlocks[b].BlockData->SectorNumber = SwapBytes64(DmgBlocks[b].BlockData->SectorNumber);
|
|
DmgBlocks[b].BlockData->SectorCount = SwapBytes64(DmgBlocks[b].BlockData->SectorCount);
|
|
DmgBlocks[b].BlockData->DataOffset = SwapBytes64(DmgBlocks[b].BlockData->DataOffset);
|
|
DmgBlocks[b].BlockData->BuffersNeeded = SwapBytes32(DmgBlocks[b].BlockData->BuffersNeeded);
|
|
DmgBlocks[b].BlockData->BlockDescriptors = SwapBytes32(DmgBlocks[b].BlockData->BlockDescriptors);
|
|
|
|
// Swap checksum.
|
|
DmgBlocks[b].BlockData->Checksum.Type = SwapBytes32(DmgBlocks[b].BlockData->Checksum.Type);
|
|
DmgBlocks[b].BlockData->Checksum.Size = SwapBytes32(DmgBlocks[b].BlockData->Checksum.Size);
|
|
for (UINTN i = 0; i < APPLE_DISK_IMAGE_CHECKSUM_SIZE; i++)
|
|
DmgBlocks[b].BlockData->Checksum.Data[i] = SwapBytes32(DmgBlocks[b].BlockData->Checksum.Data[i]);
|
|
|
|
// Swap chunks.
|
|
DmgBlocks[b].BlockData->ChunkCount = SwapBytes32(DmgBlocks[b].BlockData->ChunkCount);
|
|
for (UINT32 c = 0; c < DmgBlocks[b].BlockData->ChunkCount; c++) {
|
|
DmgBlocks[b].BlockData->Chunks[c].Type = SwapBytes32(DmgBlocks[b].BlockData->Chunks[c].Type);
|
|
DmgBlocks[b].BlockData->Chunks[c].Comment = SwapBytes32(DmgBlocks[b].BlockData->Chunks[c].Comment);
|
|
DmgBlocks[b].BlockData->Chunks[c].SectorNumber = SwapBytes64(DmgBlocks[b].BlockData->Chunks[c].SectorNumber);
|
|
DmgBlocks[b].BlockData->Chunks[c].SectorCount = SwapBytes64(DmgBlocks[b].BlockData->Chunks[c].SectorCount);
|
|
DmgBlocks[b].BlockData->Chunks[c].CompressedOffset = SwapBytes64(DmgBlocks[b].BlockData->Chunks[c].CompressedOffset);
|
|
DmgBlocks[b].BlockData->Chunks[c].CompressedLength = SwapBytes64(DmgBlocks[b].BlockData->Chunks[c].CompressedLength);
|
|
}
|
|
}
|
|
|
|
// Success.
|
|
*BlockCount = DmgBlockCount;
|
|
*Blocks = DmgBlocks;
|
|
Status = EFI_SUCCESS;
|
|
goto DONE;
|
|
|
|
DONE_ERROR:
|
|
|
|
DONE:
|
|
// Free XML.
|
|
if (XmlPlistDoc)
|
|
XmlDocumentFree(XmlPlistDoc);
|
|
if (XmlPlistBuffer)
|
|
FreePool(XmlPlistBuffer);
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
VerifyCrc32(
|
|
IN VOID *Buffer,
|
|
IN UINTN Length,
|
|
IN UINT32 Checksum) {
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
UINT32 Crc32 = 0;
|
|
|
|
// Check that parameters are valid.
|
|
if (!Buffer || !Length)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Calculate CRC32 of full image.
|
|
Status = gBS->CalculateCrc32(Buffer, Length, &Crc32);
|
|
if (EFI_ERROR(Status))
|
|
return Status;
|
|
|
|
// Ensure CRC32 matches.
|
|
if (Crc32 != Checksum)
|
|
return EFI_COMPROMISED_DATA;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
GetBlockChunk(
|
|
IN OC_APPLE_DISK_IMAGE_CONTEXT *Context,
|
|
IN EFI_LBA Lba,
|
|
OUT APPLE_DISK_IMAGE_BLOCK_DATA **Data,
|
|
OUT APPLE_DISK_IMAGE_CHUNK **Chunk) {
|
|
|
|
// Create variables.
|
|
APPLE_DISK_IMAGE_BLOCK_DATA *BlockData;
|
|
APPLE_DISK_IMAGE_CHUNK *BlockChunk;
|
|
|
|
// Search for chunk.
|
|
for (UINT32 b = 0; b < Context->BlockCount; b++) {
|
|
// Get block data.
|
|
BlockData = Context->Blocks[b].BlockData;
|
|
|
|
// Is the desired sector part of this block?
|
|
if ((Lba >= BlockData->SectorNumber) &&
|
|
(Lba < (BlockData->SectorNumber + BlockData->SectorCount))) {
|
|
// Determine chunk.
|
|
for (UINT32 c = 0; c < BlockData->ChunkCount; c++) {
|
|
// Get chunk.
|
|
BlockChunk = BlockData->Chunks + c;
|
|
|
|
// Is the desired sector part of this chunk?
|
|
if ((Lba >= DMG_SECTOR_START_ABS(BlockData, BlockChunk)) &&
|
|
(Lba < (DMG_SECTOR_START_ABS(BlockData, BlockChunk) + BlockChunk->SectorCount))) {
|
|
// Found the chunk.
|
|
*Data = BlockData;
|
|
*Chunk = BlockChunk;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Couldn't find chunk.
|
|
return EFI_NOT_FOUND;
|
|
}
|