mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
326 lines
10 KiB
C
326 lines
10 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 <Uefi.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcAppleDiskImageLib.h>
|
|
#include <Library/OcGuardLib.h>
|
|
#include <Library/OcCompressionLib.h>
|
|
|
|
#include "OcAppleDiskImageLibInternal.h"
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcAppleDiskImageInitializeContext (
|
|
IN VOID *Buffer,
|
|
IN UINTN BufferLength,
|
|
OUT OC_APPLE_DISK_IMAGE_CONTEXT **Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
OC_APPLE_DISK_IMAGE_CONTEXT *DmgContext = NULL;
|
|
UINTN DmgLength;
|
|
UINT8 *BufferBytes = NULL;
|
|
UINT8 *BufferBytesCurrent = NULL;
|
|
APPLE_DISK_IMAGE_TRAILER *BufferTrailer;
|
|
APPLE_DISK_IMAGE_TRAILER Trailer;
|
|
UINT32 DmgBlockCount;
|
|
OC_APPLE_DISK_IMAGE_BLOCK_CONTEXT *DmgBlocks = NULL;
|
|
|
|
ASSERT (Buffer != NULL);
|
|
ASSERT (BufferLength > sizeof (APPLE_DISK_IMAGE_TRAILER));
|
|
ASSERT (Context != NULL);
|
|
|
|
//
|
|
// Look for trailer signature.
|
|
//
|
|
BufferBytes = (UINT8*)Buffer;
|
|
BufferBytesCurrent = BufferBytes + BufferLength - sizeof (UINT32);
|
|
BufferTrailer = NULL;
|
|
while (BufferBytesCurrent >= BufferBytes) {
|
|
// Check for trailer signature.
|
|
if (ReadUnaligned32 ((UINT32*)BufferBytesCurrent) == SwapBytes32 (APPLE_DISK_IMAGE_MAGIC)) {
|
|
BufferTrailer = (APPLE_DISK_IMAGE_TRAILER*)BufferBytesCurrent;
|
|
DmgLength = BufferBytesCurrent - BufferBytes + sizeof (APPLE_DISK_IMAGE_TRAILER);
|
|
break;
|
|
}
|
|
|
|
// Move to previous byte.
|
|
BufferBytesCurrent--;
|
|
}
|
|
|
|
//
|
|
// If trailer not found, fail.
|
|
//
|
|
if (BufferTrailer == NULL)
|
|
return EFI_UNSUPPORTED;
|
|
|
|
//
|
|
// Get trailer.
|
|
//
|
|
CopyMem (&Trailer, BufferTrailer, sizeof (APPLE_DISK_IMAGE_TRAILER));
|
|
Trailer.Signature = SwapBytes32 (Trailer.Signature);
|
|
Trailer.Version = SwapBytes32 (Trailer.Version);
|
|
Trailer.HeaderSize = SwapBytes32 (Trailer.HeaderSize);
|
|
Trailer.Flags = SwapBytes32 (Trailer.Flags);
|
|
|
|
//
|
|
// Ensure signature and size are valid.
|
|
//
|
|
if (Trailer.Signature != APPLE_DISK_IMAGE_MAGIC ||
|
|
Trailer.HeaderSize != sizeof (APPLE_DISK_IMAGE_TRAILER)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
// Swap main fields.
|
|
Trailer.RunningDataForkOffset = SwapBytes64 (Trailer.RunningDataForkOffset);
|
|
Trailer.DataForkOffset = SwapBytes64 (Trailer.DataForkOffset);
|
|
Trailer.DataForkLength = SwapBytes64 (Trailer.DataForkLength);
|
|
Trailer.RsrcForkOffset = SwapBytes64 (Trailer.RsrcForkOffset);
|
|
Trailer.RsrcForkLength = SwapBytes64 (Trailer.RsrcForkLength);
|
|
Trailer.SegmentNumber = SwapBytes32 (Trailer.SegmentNumber);
|
|
Trailer.SegmentCount = SwapBytes32 (Trailer.SegmentCount);
|
|
|
|
// Swap data fork checksum.
|
|
Trailer.DataForkChecksum.Type = SwapBytes32 (Trailer.DataForkChecksum.Type);
|
|
Trailer.DataForkChecksum.Size = SwapBytes32 (Trailer.DataForkChecksum.Size);
|
|
for (UINTN i = 0; i < APPLE_DISK_IMAGE_CHECKSUM_SIZE; i++)
|
|
Trailer.DataForkChecksum.Data[i] = SwapBytes32 (Trailer.DataForkChecksum.Data[i]);
|
|
|
|
// Swap XML info.
|
|
Trailer.XmlOffset = SwapBytes64 (Trailer.XmlOffset);
|
|
Trailer.XmlLength = SwapBytes64 (Trailer.XmlLength);
|
|
|
|
// Swap main checksum.
|
|
Trailer.Checksum.Type = SwapBytes32 (Trailer.Checksum.Type);
|
|
Trailer.Checksum.Size = SwapBytes32 (Trailer.Checksum.Size);
|
|
for (UINTN i = 0; i < APPLE_DISK_IMAGE_CHECKSUM_SIZE; i++)
|
|
Trailer.Checksum.Data[i] = SwapBytes32 (Trailer.Checksum.Data[i]);
|
|
|
|
// Swap addition fields.
|
|
Trailer.ImageVariant = SwapBytes32 (Trailer.ImageVariant);
|
|
Trailer.SectorCount = SwapBytes64 (Trailer.SectorCount);
|
|
|
|
// If data fork checksum is CRC32, verify it.
|
|
if (Trailer.DataForkChecksum.Type == APPLE_DISK_IMAGE_CHECKSUM_TYPE_CRC32) {
|
|
Status = VerifyCrc32 (((UINT8*)Buffer) + Trailer.DataForkOffset,
|
|
Trailer.DataForkLength, Trailer.DataForkChecksum.Data[0]);
|
|
if (EFI_ERROR (Status))
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Ensure XML offset/length is valid and in range.
|
|
//
|
|
if (Trailer.XmlOffset == 0 || Trailer.XmlOffset >= (DmgLength - sizeof (APPLE_DISK_IMAGE_TRAILER)) ||
|
|
Trailer.XmlLength == 0 || (Trailer.XmlOffset + Trailer.XmlLength) > (DmgLength - sizeof (APPLE_DISK_IMAGE_TRAILER))) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
//
|
|
// Parse XML.
|
|
//
|
|
Status = ParsePlist (Buffer, Trailer.XmlOffset, Trailer.XmlLength, &DmgBlockCount, &DmgBlocks);
|
|
if (EFI_ERROR(Status))
|
|
goto DONE_ERROR;
|
|
|
|
//
|
|
// Allocate DMG file structure.
|
|
//
|
|
DmgContext = AllocateZeroPool (sizeof (OC_APPLE_DISK_IMAGE_CONTEXT));
|
|
if (!DmgContext) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto DONE_ERROR;
|
|
}
|
|
|
|
// Fill DMG file structure.
|
|
DmgContext->Buffer = Buffer;
|
|
DmgContext->Length = DmgLength;
|
|
CopyMem (&(DmgContext->Trailer), &Trailer, sizeof (APPLE_DISK_IMAGE_TRAILER));
|
|
DmgContext->BlockCount = DmgBlockCount;
|
|
DmgContext->Blocks = DmgBlocks;
|
|
|
|
*Context = DmgContext;
|
|
Status = EFI_SUCCESS;
|
|
goto DONE;
|
|
|
|
DONE_ERROR:
|
|
if (DmgBlocks != NULL)
|
|
FreePool (DmgBlocks);
|
|
|
|
DONE:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcAppleDiskImageFreeContext (
|
|
IN OC_APPLE_DISK_IMAGE_CONTEXT *Context
|
|
)
|
|
{
|
|
UINT64 Index;
|
|
OC_APPLE_DISK_IMAGE_BLOCK_CONTEXT *CurrentBlockContext;
|
|
|
|
ASSERT (Context != NULL);
|
|
|
|
// Free blocks.
|
|
if (Context->Blocks) {
|
|
for (Index = 0; Index < Context->BlockCount; Index++) {
|
|
// Get block.
|
|
CurrentBlockContext = Context->Blocks + Index;
|
|
|
|
// Free block data.
|
|
if (CurrentBlockContext->CfName != NULL)
|
|
FreePool (CurrentBlockContext->CfName);
|
|
if (CurrentBlockContext->Name != NULL)
|
|
FreePool (CurrentBlockContext->Name);
|
|
if (CurrentBlockContext->BlockData != NULL)
|
|
FreePool(CurrentBlockContext->BlockData);
|
|
}
|
|
FreePool (Context->Blocks);
|
|
}
|
|
|
|
FreePool (Context);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
OcAppleDiskImageRead(
|
|
IN OC_APPLE_DISK_IMAGE_CONTEXT *Context,
|
|
IN EFI_LBA Lba,
|
|
IN UINTN BufferSize,
|
|
OUT VOID *Buffer) {
|
|
|
|
// Create variables.
|
|
EFI_STATUS Status;
|
|
|
|
// Chunk to read.
|
|
APPLE_DISK_IMAGE_BLOCK_DATA *BlockData;
|
|
APPLE_DISK_IMAGE_CHUNK *Chunk;
|
|
UINTN ChunkTotalLength;
|
|
UINTN ChunkLength;
|
|
UINTN ChunkOffset;
|
|
UINT8 *ChunkData;
|
|
UINT8 *ChunkDataCurrent;
|
|
|
|
// Buffer.
|
|
EFI_LBA LbaCurrent;
|
|
EFI_LBA LbaOffset;
|
|
EFI_LBA LbaLength;
|
|
UINTN RemainingBufferSize;
|
|
UINTN BufferChunkSize;
|
|
UINT8 *BufferCurrent;
|
|
|
|
// zlib data.
|
|
UINTN OutSize;
|
|
|
|
// Check if parameters are valid.
|
|
if (!Context || !Buffer)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Check that sector is in range.
|
|
if (Lba >= Context->Trailer.SectorCount)
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
// Read blocks.
|
|
LbaCurrent = Lba;
|
|
RemainingBufferSize = BufferSize;
|
|
BufferCurrent = Buffer;
|
|
while (RemainingBufferSize) {
|
|
// Determine block in DMG.
|
|
Status = GetBlockChunk(Context, LbaCurrent, &BlockData, &Chunk);
|
|
if (EFI_ERROR(Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto DONE;
|
|
}
|
|
|
|
// Determine offset into source DMG.
|
|
LbaOffset = LbaCurrent - DMG_SECTOR_START_ABS(BlockData, Chunk);
|
|
LbaLength = Chunk->SectorCount - LbaOffset;
|
|
ChunkOffset = LbaOffset * APPLE_DISK_IMAGE_SECTOR_SIZE;
|
|
ChunkTotalLength = (UINTN)Chunk->SectorCount * APPLE_DISK_IMAGE_SECTOR_SIZE;
|
|
ChunkLength = ChunkTotalLength - ChunkOffset;
|
|
|
|
// If the buffer size is bigger than the chunk, there will be more chunks to get.
|
|
BufferChunkSize = RemainingBufferSize;
|
|
if (BufferChunkSize > ChunkLength)
|
|
BufferChunkSize = ChunkLength;
|
|
|
|
// Determine type.
|
|
switch(Chunk->Type) {
|
|
// No data, write zeroes.
|
|
case APPLE_DISK_IMAGE_CHUNK_TYPE_ZERO:
|
|
case APPLE_DISK_IMAGE_CHUNK_TYPE_IGNORE:
|
|
// Zero destination buffer.
|
|
ZeroMem(BufferCurrent, BufferChunkSize);
|
|
break;
|
|
|
|
// Raw data, write data as-is.
|
|
case APPLE_DISK_IMAGE_CHUNK_TYPE_RAW:
|
|
// Determine pointer to source data.
|
|
ChunkData = (Context->Buffer + BlockData->DataOffset + Chunk->CompressedOffset);
|
|
ChunkDataCurrent = ChunkData + ChunkOffset;
|
|
|
|
// Copy to destination buffer.
|
|
CopyMem(BufferCurrent, ChunkDataCurrent, BufferChunkSize);
|
|
ChunkData = ChunkDataCurrent = NULL;
|
|
break;
|
|
|
|
// zlib-compressed data, inflate and write uncompressed data.
|
|
case APPLE_DISK_IMAGE_CHUNK_TYPE_ZLIB:
|
|
// Allocate buffer for inflated data.
|
|
ChunkData = AllocateZeroPool(ChunkTotalLength);
|
|
ChunkDataCurrent = ChunkData + ChunkOffset;
|
|
OutSize = DecompressZLIB (
|
|
ChunkData,
|
|
ChunkTotalLength,
|
|
(Context->Buffer + BlockData->DataOffset + Chunk->CompressedOffset),
|
|
Chunk->CompressedLength
|
|
);
|
|
if (OutSize != ChunkTotalLength) {
|
|
FreePool (ChunkData);
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto DONE;
|
|
}
|
|
|
|
// Copy to destination buffer.
|
|
CopyMem(BufferCurrent, ChunkDataCurrent, BufferChunkSize);
|
|
FreePool(ChunkData);
|
|
ChunkData = ChunkDataCurrent = NULL;
|
|
break;
|
|
|
|
// Unknown chunk type.
|
|
default:
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto DONE;
|
|
}
|
|
|
|
// Move to next chunk.
|
|
RemainingBufferSize -= BufferChunkSize;
|
|
BufferCurrent += BufferChunkSize;
|
|
LbaCurrent += LbaLength;
|
|
}
|
|
|
|
// Success.
|
|
Status = EFI_SUCCESS;
|
|
|
|
DONE:
|
|
return Status;
|
|
}
|