From c3e14b6824fc053285f4f4e3c3634d2d240625d2 Mon Sep 17 00:00:00 2001 From: vit9696 <4348897+vit9696@users.noreply.github.com> Date: Sat, 15 Aug 2020 00:40:06 +0300 Subject: [PATCH] OcAppleKernelLib: Implement mkext injection support (#99) --- .../Acidanthera/Library/OcAppleKernelLib.h | 225 ++++ Include/Acidanthera/Library/OcMachoLib.h | 72 + Library/OcAppleKernelLib/MkextContext.c | 1171 +++++++++++++++++ Library/OcAppleKernelLib/MkextReader.c | 132 ++ Library/OcAppleKernelLib/OcAppleKernelLib.inf | 2 + Library/OcAppleKernelLib/PrelinkedContext.c | 2 +- Library/OcMachoLib/CxxSymbols.c | 2 +- Library/OcMachoLib/Fat.c | 151 +++ Library/OcMachoLib/Header.c | 86 +- Library/OcMachoLib/OcMachoLib.inf | 1 + Library/OcMachoLib/Relocations.c | 2 +- Library/OcMachoLib/Symbols.c | 2 +- Platform/OpenCore/OpenCoreKernel.c | 212 ++- User/Makefile | 2 +- 14 files changed, 1961 insertions(+), 101 deletions(-) create mode 100644 Library/OcAppleKernelLib/MkextContext.c create mode 100644 Library/OcAppleKernelLib/MkextReader.c create mode 100644 Library/OcMachoLib/Fat.c diff --git a/Include/Acidanthera/Library/OcAppleKernelLib.h b/Include/Acidanthera/Library/OcAppleKernelLib.h index 0a195bf6..669ffd1b 100644 --- a/Include/Acidanthera/Library/OcAppleKernelLib.h +++ b/Include/Acidanthera/Library/OcAppleKernelLib.h @@ -15,6 +15,7 @@ #ifndef OC_APPLE_KERNEL_LIB_H #define OC_APPLE_KERNEL_LIB_H +#include #include #include #include @@ -48,7 +49,14 @@ #define OS_BUNDLE_REQUIRED_SAFE_BOOT "Safe Boot" +#define MKEXT_INFO_DICTIONARIES_KEY "_MKEXTInfoDictionaries" +#define MKEXT_BUNDLE_PATH_KEY "_MKEXTBundlePath" +#define MKEXT_EXECUTABLE_RELATIVE_PATH_KEY "_MKEXTExecutableRelativePath" +#define MKEXT_EXECUTABLE_KEY "_MKEXTExecutable" + + #define PRELINK_INFO_INTEGER_ATTRIBUTES "size=\"64\"" +#define MKEXT_INFO_INTEGER_ATTRIBUTES "size=\"32\"" #define KC_REGION_SEGMENT_PREFIX "__REGION" #define KC_REGION0_SEGMENT "__REGION0" @@ -66,6 +74,12 @@ // #define PRELINK_INFO_RESERVE_SIZE (5U * 1024U * 1024U) +// +// Size to reserve per kext for plist expansion. +// Additional properties are added to prelinked and mkext v2, this should account for those. +// +#define PLIST_EXPANSION_SIZE 512 + // // Prelinked context used for kernel modification. // @@ -285,6 +299,62 @@ typedef struct { BOOLEAN BuiltInKextsValid; } CACHELESS_CONTEXT; +// +// Mkext context. +// +typedef struct { + // + // Current version of mkext. It takes a reference of user-allocated + // memory block from pool, and grows if needed. + // + UINT8 *Mkext; + // + // Exportable mkext size, i.e. the payload size. Also references user field. + // + UINT32 MkextSize; + // + // Currently allocated mkext size, used for reduced rellocations. + // + UINT32 MkextAllocSize; + // + // Mkext header. + // + MKEXT_HEADER_ANY *MkextHeader; + // + // Version. + // + UINT32 MkextVersion; + // + // CPU type. + // + BOOLEAN Is64Bit; + // + // Current number of kexts. + // + UINT32 NumKexts; + // + // Max kexts for allocation. + // + UINT32 NumMaxKexts; + // + // Offset of mkext plist. + // + UINT32 MkextInfoOffset; + // + // Copy of mkext plist used for XML_DOCUMENT. + // Freed upon context destruction. + // + UINT8 *MkextInfo; + // + // Parsed instance of mkext plist. New entries are added here. + // + XML_DOCUMENT *MkextInfoDocument; + // + // Array of kexts. + // + XML_NODE *MkextKexts; +} MKEXT_CONTEXT; + /** Read Apple kernel for target architecture (possibly decompressing) into pool allocated buffer. @@ -874,4 +944,159 @@ CachelessContextHookBuiltin ( OUT EFI_FILE_PROTOCOL **VirtualFile ); +/** + Decompress mkext buffer while reserving space for injected kexts later on. + Specifying zero for OutBufferSize will calculate the size of the + buffer required for the decompressed mkext in OutMkextSize. + + @param[in] Buffer Mkext buffer. + @param[in] BufferSize Mkext buffer size. + @param[in] NumReservedKexts Number of kext slots to reserve for injection. + @param[in,out] OutBuffer Output buffer. Optional if OutBufferSize is zero. + @param[in] OutBufferSize Total output buffer size. Specify zero to + calculate output buffer size. + @param[in,out] OutMkextSize Decompressed Mkext size. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +MkextDecompress ( + IN CONST UINT8 *Buffer, + IN UINT32 BufferSize, + IN UINT32 NumReservedKexts, + IN OUT UINT8 *OutBuffer OPTIONAL, + IN UINT32 OutBufferSize OPTIONAL, + IN OUT UINT32 *OutMkextSize + ); + +/** + Check if passed mkext is of desired CPU arch. + + @param[in] Mkext Mkext buffer. + @param[in] MkextSize Mkext buffer size. + @param[in] CpuType Desired CPU arch. + + @return FALSE if mismatched arch or invalid mkext. +**/ +BOOLEAN +MkextCheckCpuType ( + IN UINT8 *Mkext, + IN UINT32 MkextSize, + IN MACH_CPU_TYPE CpuType + ); + +/** + Construct mkext context for later modification. + Must be freed with MkextContextFree on success. + Note that MkextAllocSize never changes, and is to be estimated. + + Mkext buffers cannot contain any compression, and should be run + through MkextDecompress first. + + @param[in,out] Context Mkext context. + @param[in,out] Mkext Decompressed Mkext buffer. + @param[in] MkextSize Decompressed Mkext buffer size. + @param[in] MkextAllocSize Decompressed Mkext buffer allocated size. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +MkextContextInit ( + IN OUT MKEXT_CONTEXT *Context, + IN OUT UINT8 *Mkext, + IN UINT32 MkextSize, + IN UINT32 MkextAllocSize + ); + +/** + Free resources consumed by mkext context. + + @param[in,out] Context Mkext context. +**/ +VOID +MkextContextFree ( + IN OUT MKEXT_CONTEXT *Context + ); + +/** + Updated required mkext reserve size to inject this kext. + + @param[in,out] ReservedInfoSize Current reserved PLIST size, updated. + @param[in,out] ReservedExeSize Current reserved KEXT size, updated. + @param[in] InfoPlistSize Kext Info.plist size. + @param[in] Executable Kext executable, optional. + @param[in] ExecutableSize Kext executable size, optional. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +MkextReserveKextSize ( + IN OUT UINT32 *ReservedInfoSize, + IN OUT UINT32 *ReservedExeSize, + IN UINT32 InfoPlistSize, + IN UINT8 *Executable, + IN UINT32 ExecutableSize OPTIONAL + ); + +/** + Perform mkext kext injection. + + @param[in,out] Context Mkext context. + @param[in] BundlePath Kext bundle path (e.g. /L/E/mykext.kext). + @param[in,out] InfoPlist Kext Info.plist. + @param[in] InfoPlistSize Kext Info.plist size. + @param[in,out] Executable Kext executable, optional. + @param[in] ExecutableSize Kext executable size, optional. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +MkextInjectKext ( + IN OUT MKEXT_CONTEXT *Context, + IN CONST CHAR8 *BundlePath, + IN CONST CHAR8 *InfoPlist, + IN UINT32 InfoPlistSize, + IN UINT8 *Executable OPTIONAL, + IN UINT32 ExecutableSize OPTIONAL + ); + +/** + Refresh plist and checksum after kext + injection and/or patching. + + @param[in,out] Context Mkext context. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +MkextInjectPatchComplete ( + IN OUT MKEXT_CONTEXT *Context + ); + +/** + Read mkext for target architecture (possibly decompressing) + into pool allocated buffer. If CpuType does not exist in fat + mkext, an error is returned. + + @param[in] File File handle instance. + @param[in] CpuType Desired architecture of mkext. + @param[in,out] Mkext Resulting non-fat mkext buffer from pool. + @param[out] MkextSize Actual mkext size. + @param[out] AllocatedSize Allocated mkext size (AllocatedSize >= MkextSize). + @param[in] ReservedSize Allocated extra size for added kernel extensions. + @param[in] NumReservedKexts Number of kext slots to reserve in mkext. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +ReadAppleMkext ( + IN EFI_FILE_PROTOCOL *File, + IN MACH_CPU_TYPE CpuType, + OUT UINT8 **Mkext, + OUT UINT32 *MkextSize, + OUT UINT32 *AllocatedSize, + IN UINT32 ReservedSize, + IN UINT32 NumReservedKexts + ); + #endif // OC_APPLE_KERNEL_LIB_H diff --git a/Include/Acidanthera/Library/OcMachoLib.h b/Include/Acidanthera/Library/OcMachoLib.h index 67764f1a..2509cfc9 100644 --- a/Include/Acidanthera/Library/OcMachoLib.h +++ b/Include/Acidanthera/Library/OcMachoLib.h @@ -808,4 +808,76 @@ MachoGetNextCommand64 ( IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL ); +/** + Returns offset and size of specified slice in case + FAT Mach-O is used. If no FAT is detected, FatOffset and + FatSize are set to 0 and FullSize respectively. + + @param[in] Buffer Pointer to the buffer data. + @param[in] BufferSize Size of Buffer. + @param[in] FullSize Full file size, used to validate sizes + within FAT structure. + @param[in] CpuType Desired CPU slice to use. + @param[out] FatOffset Pointer to offset of FAT slice. + @param[out] FatSize Pointer to size of FAT slice. + + @return EFI_SUCCESS if no FAT, or arch was found in valid FAT image. +**/ +EFI_STATUS +FatGetArchitectureOffset ( + IN CONST UINT8 *Buffer, + IN UINT32 BufferSize, + IN UINT32 FullSize, + IN MACH_CPU_TYPE CpuType, + OUT UINT32 *FatOffset, + OUT UINT32 *FatSize + ); + +/** + Moves file pointer and size to point to specified slice in case + FAT Mach-O is used. + + @param[in,out] FileData Pointer to pointer of the file's data. + @param[in,out] FileSize Pointer to file size of FileData. + @param[in] CpuType Desired CPU slice to use. + + @return EFI_SUCCESS if no FAT, or arch was found in valid FAT image. +**/ +EFI_STATUS +FatFilterArchitectureByType ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize, + IN MACH_CPU_TYPE CpuType + ); + +/** + Moves file pointer and size to point to x86 slice in case + FAT Mach-O is used. + + @param[in,out] FileData Pointer to pointer of the file's data. + @param[in,out] FileSize Pointer to file size of FileData. + + @return EFI_SUCCESS if no FAT, or a valid 32-bit arch exists. +**/ +EFI_STATUS +FatFilterArchitecture32 ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize + ); + +/** + Moves file pointer and size to point to x86_64 slice in case + FAT Mach-O is used. + + @param[in,out] FileData Pointer to pointer of the file's data. + @param[in,out] FileSize Pointer to file size of FileData. + + @return EFI_SUCCESS if no FAT, or a valid 64-bit arch exists. +**/ +EFI_STATUS +FatFilterArchitecture64 ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize + ); + #endif // OC_MACHO_LIB_H diff --git a/Library/OcAppleKernelLib/MkextContext.c b/Library/OcAppleKernelLib/MkextContext.c new file mode 100644 index 00000000..4dc3e113 --- /dev/null +++ b/Library/OcAppleKernelLib/MkextContext.c @@ -0,0 +1,1171 @@ +/** @file + Mkext support. + + Copyright (c) 2020, 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 + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MKEXT_OFFSET_STR_LEN 24 + +// +// Alignment to 8 bytes. +// +#define MKEXT_ALIGN(a) (ALIGN_VALUE (a, sizeof (UINT64))) + +STATIC +BOOLEAN +ParseKextBinary ( + IN OUT UINT8 **Buffer, + IN OUT UINT32 *BufferSize, + IN BOOLEAN Is64Bit + ) +{ + EFI_STATUS Status; + MACH_HEADER_ANY *MachHeader; + + Status = FatFilterArchitectureByType (Buffer, BufferSize, Is64Bit ? MachCpuTypeX8664 : MachCpuTypeI386); + if (EFI_ERROR (Status)) { + return FALSE; + } + + // + // Size/alignment checked by FatFilterArchitectureByType. + // + MachHeader = (MACH_HEADER_ANY *)* Buffer; + + if ((!Is64Bit && MachHeader->Signature == MACH_HEADER_SIGNATURE) + || (Is64Bit && MachHeader->Signature == MACH_HEADER_64_SIGNATURE)) { + return TRUE; + } + + return FALSE; +} + +STATIC +VOID +UpdateMkextLengthChecksum ( + IN MKEXT_HEADER_ANY *Mkext, + IN UINT32 Length + ) +{ + Mkext->Common.Length = SwapBytes32 (Length); + Mkext->Common.Adler32 = SwapBytes32 ( + Adler32 ((UINT8*)&Mkext->Common.Version, + Length - OFFSET_OF (MKEXT_CORE_HEADER, Version)) + ); +} + +STATIC +BOOLEAN +ParseMkextV2Plist ( + IN MKEXT_V2_HEADER *Mkext, + OUT UINT8 **Plist, + OUT XML_DOCUMENT **PlistDoc, + OUT XML_NODE **PlistBundles + ) +{ + UINT8 *MkextBuffer; + UINT32 MkextLength; + VOID *PlistBuffer; + XML_DOCUMENT *PlistXml; + UINT32 PlistOffset; + UINT32 PlistCompressedSize; + UINT32 PlistFullSize; + UINT32 PlistStoredSize; + UINT32 Tmp; + + XML_NODE *MkextInfoRoot; + UINT32 MkextInfoRootIndex; + UINT32 MkextInfoRootCount; + + XML_NODE *PlistBundleArray; + CONST CHAR8 *BundleArrayKey; + + + MkextBuffer = (UINT8 *) Mkext; + MkextLength = SwapBytes32 (Mkext->Header.Length); + PlistOffset = SwapBytes32 (Mkext->PlistOffset); + PlistCompressedSize = SwapBytes32 (Mkext->PlistCompressedSize); + PlistFullSize = SwapBytes32 (Mkext->PlistFullSize); + + PlistStoredSize = PlistCompressedSize; + if (PlistStoredSize == 0) { + PlistStoredSize = PlistFullSize; + } + + if (OcOverflowAddU32 (PlistOffset, PlistStoredSize, &Tmp) || Tmp > MkextLength) { + return FALSE; + } + + PlistBuffer = AllocatePool (PlistFullSize); + if (PlistBuffer == NULL) { + return FALSE; + } + + // + // Copy/decompress plist. + // + if (PlistCompressedSize > 0) { + if (DecompressZLIB ( + PlistBuffer, + PlistFullSize, + &MkextBuffer[PlistOffset], + PlistCompressedSize + ) != PlistFullSize) { + FreePool (PlistBuffer); + return FALSE; + } + } else { + CopyMem (PlistBuffer, &MkextBuffer[PlistOffset], PlistFullSize); + } + + PlistXml = XmlDocumentParse (PlistBuffer, PlistFullSize, FALSE); + if (PlistXml == NULL) { + FreePool (PlistBuffer); + return FALSE; + } + + // + // Mkext v2 root element is a dictionary containing an array of bundles. + // + MkextInfoRoot = PlistNodeCast (XmlDocumentRoot (PlistXml), PLIST_NODE_TYPE_DICT); + if (MkextInfoRoot == NULL) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return FALSE; + } + + // + // Get bundle array. + // + MkextInfoRootCount = PlistDictChildren (MkextInfoRoot); + for (MkextInfoRootIndex = 0; MkextInfoRootIndex < MkextInfoRootCount; MkextInfoRootIndex++) { + BundleArrayKey = PlistKeyValue (PlistDictChild (MkextInfoRoot, MkextInfoRootIndex, &PlistBundleArray)); + if (BundleArrayKey == NULL) { + continue; + } + + if (AsciiStrCmp (BundleArrayKey, MKEXT_INFO_DICTIONARIES_KEY) == 0) { + *Plist = PlistBuffer; + *PlistDoc = PlistXml; + *PlistBundles = PlistBundleArray; + + return TRUE; + } + } + + // + // No bundle array found. + // + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return FALSE; +} + +STATIC +UINT32 +UpdateMkextV2Plist ( + IN OUT MKEXT_V2_HEADER *Mkext, + IN UINT32 AllocatedSize, + IN XML_DOCUMENT *PlistDoc, + IN UINT32 Offset + ) +{ + UINT8 *MkextBuffer; + CHAR8 *ExportedInfo; + UINT32 ExportedInfoSize; + UINT32 TmpSize; + + // + // Export plist and include \0 terminator in size. + // + ExportedInfo = XmlDocumentExport (PlistDoc, &ExportedInfoSize, 0, FALSE); + if (ExportedInfo == NULL) { + return 0; + } + ExportedInfoSize++; + + if (OcOverflowAddU32 (Offset, ExportedInfoSize, &TmpSize) + || TmpSize > AllocatedSize) { + FreePool (ExportedInfo); + return 0; + } + + MkextBuffer = (UINT8*)Mkext; + CopyMem (&MkextBuffer[Offset], ExportedInfo, ExportedInfoSize); + FreePool (ExportedInfo); + + Mkext->PlistOffset = SwapBytes32 (Offset); + Mkext->PlistFullSize = SwapBytes32 (ExportedInfoSize); + Mkext->PlistCompressedSize = 0; + return ExportedInfoSize; +} + +EFI_STATUS +MkextDecompress ( + IN CONST UINT8 *Buffer, + IN UINT32 BufferSize, + IN UINT32 NumReservedKexts, + IN OUT UINT8 *OutBuffer OPTIONAL, + IN UINT32 OutBufferSize OPTIONAL, + IN OUT UINT32 *OutMkextSize + ) +{ + BOOLEAN Decompress; + + MKEXT_HEADER_ANY *MkextHeader; + MKEXT_HEADER_ANY *MkextHeaderOut; + UINT32 MkextSize; + UINT32 MkextVersion; + UINT32 NumKexts; + UINT32 NumMaxKexts; + + UINT32 Tmp; + UINT32 Index; + UINT32 CurrentOffset; + UINT32 NewOffset; + UINT32 PlistOffset; + UINT32 PlistCompSize; + UINT32 PlistFullSize; + UINT32 PlistFullSizeAligned; + UINT32 BinOffset; + UINT32 BinCompSize; + UINT32 BinFullSize; + UINT32 BinFullSizeAligned; + + UINT8 *PlistBuffer; + XML_DOCUMENT *PlistXml; + XML_NODE *PlistBundles; + UINT32 PlistBundlesCount; + XML_NODE *PlistBundle; + UINT32 PlistBundleIndex; + UINT32 PlistBundleCount; + CONST CHAR8 *PlistBundleKey; + XML_NODE *BundleExecutable; + + MKEXT_V2_FILE_ENTRY *MkextExecutableEntry; + MKEXT_V2_FILE_ENTRY *MkextOutExecutableEntry; + CHAR8 *BinaryOffsetStrings; + UINT32 BinaryOffsetStringsSize; + + ASSERT (Buffer != NULL); + ASSERT (BufferSize > 0); + + Decompress = OutBufferSize > 0; + if (Decompress) { + ASSERT (OutBuffer != NULL); + ASSERT (OutMkextSize != NULL); + } + + if (BufferSize < sizeof (MKEXT_CORE_HEADER) + || !OC_TYPE_ALIGNED (MKEXT_CORE_HEADER, Buffer)) { + return EFI_INVALID_PARAMETER; + } + + MkextHeader = (MKEXT_HEADER_ANY *) Buffer; + MkextSize = SwapBytes32 (MkextHeader->Common.Length); + MkextVersion = SwapBytes32 (MkextHeader->Common.Version); + NumKexts = SwapBytes32 (MkextHeader->Common.NumKexts); + + if (MkextHeader->Common.Magic != MKEXT_INVERT_MAGIC + || MkextHeader->Common.Signature != MKEXT_INVERT_SIGNATURE + || BufferSize != MkextSize + || OcOverflowAddU32 (NumKexts, NumReservedKexts, &NumMaxKexts)) { + return EFI_INVALID_PARAMETER; + } + + // + // Mkext v1. + // + if (MkextVersion == MKEXT_VERSION_V1) { + // + // Validate header and array size. + // We need to start our offset after the header including reserved kext slots. + // + if (OcOverflowMulAddU32 (sizeof (MKEXT_V1_KEXT), NumKexts, sizeof (MKEXT_V1_HEADER), &Tmp) + || Tmp > MkextSize + || OcOverflowMulAddU32 (sizeof (MKEXT_V1_KEXT), NumMaxKexts, sizeof (MKEXT_V1_HEADER), &CurrentOffset) + || MKEXT_ALIGN (CurrentOffset) < CurrentOffset) { + return EFI_INVALID_PARAMETER; + } + CurrentOffset = MKEXT_ALIGN (CurrentOffset); + + if (Decompress) { + // + // When decompressing, CurrentOffset needs to be within bounds of OutBufferSize. + // If not decompressing, this is unnecessary as we are calculating decompressed size only. + // + if (CurrentOffset > OutBufferSize) { + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy header. + // + CopyMem (OutBuffer, Buffer, sizeof (MKEXT_V1_HEADER)); + MkextHeaderOut = (MKEXT_HEADER_ANY *) OutBuffer; + } + + // + // Process plists and binaries if present, decompressing as needed. + // + for (Index = 0; Index < NumKexts; Index++) { + PlistFullSize = SwapBytes32 (MkextHeader->V1.Kexts[Index].Plist.FullSize); + BinFullSize = SwapBytes32 (MkextHeader->V1.Kexts[Index].Binary.FullSize); + PlistFullSizeAligned = MKEXT_ALIGN (PlistFullSize); + BinFullSizeAligned = MKEXT_ALIGN (BinFullSize); + + if (PlistFullSizeAligned < PlistFullSize + || BinFullSizeAligned < BinFullSize) { + return EFI_INVALID_PARAMETER; + } + + if (Decompress) { + // + // Decompress entry. + // + PlistOffset = SwapBytes32 (MkextHeader->V1.Kexts[Index].Plist.Offset); + PlistCompSize = SwapBytes32 (MkextHeader->V1.Kexts[Index].Plist.CompressedSize); + BinOffset = SwapBytes32 (MkextHeader->V1.Kexts[Index].Binary.Offset); + BinCompSize = SwapBytes32 (MkextHeader->V1.Kexts[Index].Binary.CompressedSize); + + if (OcOverflowTriAddU32 (CurrentOffset, PlistFullSizeAligned, BinFullSizeAligned, &Tmp)) { + return EFI_INVALID_PARAMETER; + } + if (Tmp > OutBufferSize) { + return EFI_BUFFER_TOO_SMALL; + } + + // + // Compressed size == 0 means no compression. + // + if (PlistCompSize > 0) { + if (OcOverflowAddU32 (PlistOffset, PlistCompSize, &Tmp) + || Tmp > MkextSize + || DecompressLZSS ( + &OutBuffer[CurrentOffset], + PlistFullSize, + &((UINT8*)Buffer)[PlistOffset], + PlistCompSize + ) != PlistFullSize) { + return EFI_INVALID_PARAMETER; + } + } else { + if (OcOverflowAddU32 (PlistOffset, PlistFullSize, &Tmp) + || Tmp > MkextSize) { + return EFI_INVALID_PARAMETER; + } + CopyMem (&OutBuffer[CurrentOffset], &Buffer[PlistOffset], PlistFullSize); + } + + MkextHeaderOut->V1.Kexts[Index].Plist.Offset = SwapBytes32 (CurrentOffset); + MkextHeaderOut->V1.Kexts[Index].Plist.CompressedSize = 0; + MkextHeaderOut->V1.Kexts[Index].Plist.FullSize = SwapBytes32 (PlistFullSize); + MkextHeaderOut->V1.Kexts[Index].Plist.ModifiedSeconds = MkextHeader->V1.Kexts[Index].Plist.ModifiedSeconds; + CurrentOffset += PlistFullSizeAligned; + + if (BinFullSize > 0) { + // + // Compressed size == 0 means no compression. + // + if (BinCompSize > 0) { + if (OcOverflowAddU32 (BinOffset, BinCompSize, &Tmp) + || Tmp > MkextSize + || DecompressLZSS ( + &OutBuffer[CurrentOffset], + BinFullSize, + &((UINT8*)Buffer)[BinOffset], + BinCompSize + ) != BinFullSize) { + return EFI_INVALID_PARAMETER; + } + } else { + if (OcOverflowAddU32 (BinOffset, BinFullSize, &Tmp) + || Tmp > MkextSize) { + return EFI_INVALID_PARAMETER; + } + CopyMem (&OutBuffer[CurrentOffset], &Buffer[BinOffset], BinFullSize); + } + + MkextHeaderOut->V1.Kexts[Index].Binary.Offset = SwapBytes32 (CurrentOffset); + MkextHeaderOut->V1.Kexts[Index].Binary.CompressedSize = 0; + MkextHeaderOut->V1.Kexts[Index].Binary.FullSize = SwapBytes32 (BinFullSize); + MkextHeaderOut->V1.Kexts[Index].Binary.ModifiedSeconds = MkextHeader->V1.Kexts[Index].Binary.ModifiedSeconds; + CurrentOffset += BinFullSizeAligned; + } else { + ZeroMem (&MkextHeaderOut->V1.Kexts[Index].Binary, sizeof (MKEXT_V1_KEXT_FILE)); + } + + } else { + // + // Calculate size only. + // + if (OcOverflowTriAddU32 ( + CurrentOffset, + PlistFullSizeAligned, + BinFullSizeAligned, + &CurrentOffset + )) { + return EFI_INVALID_PARAMETER; + } + } + } + + *OutMkextSize = CurrentOffset; + + // + // Mkext v2. + // + } else if (MkextVersion == MKEXT_VERSION_V2) { + if (MkextSize < sizeof (MKEXT_V2_HEADER)) { + return EFI_INVALID_PARAMETER; + } + CurrentOffset = MKEXT_ALIGN (sizeof (MKEXT_V2_HEADER)); + + if (Decompress) { + // + // When decompressing, CurrentOffset needs to be within bounds of OutBufferSize. + // If not decompressing, this is unnecessary as we are calculating decompressed size only. + // + if (CurrentOffset > OutBufferSize) { + return EFI_BUFFER_TOO_SMALL; + } + + // + // Copy header. + // + CopyMem (OutBuffer, Buffer, sizeof (MKEXT_V2_HEADER)); + MkextHeaderOut = (MKEXT_HEADER_ANY *) OutBuffer; + } + + if (!ParseMkextV2Plist (&MkextHeader->V2, &PlistBuffer, &PlistXml, &PlistBundles)) { + return EFI_INVALID_PARAMETER; + } + + // + // All offset strings are kept in array until the plist is regenerated. + // + PlistBundlesCount = XmlNodeChildren (PlistBundles); + BinaryOffsetStrings = NULL; + + if (Decompress) { + if (OcOverflowTriMulU32 (PlistBundlesCount, MKEXT_OFFSET_STR_LEN, sizeof (CHAR8), &BinaryOffsetStringsSize)) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + + BinaryOffsetStrings = AllocateZeroPool (BinaryOffsetStringsSize); + if (BinaryOffsetStrings == NULL) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_OUT_OF_RESOURCES; + } + } + + // + // Enumerate bundles. + // + for (Index = 0; Index < PlistBundlesCount; Index++) { + PlistBundle = PlistNodeCast (XmlNodeChild (PlistBundles, Index), PLIST_NODE_TYPE_DICT); + if (PlistBundle == NULL) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + if (Decompress) { + FreePool (BinaryOffsetStrings); + } + return EFI_INVALID_PARAMETER; + } + + // + // Get executable information, if present. + // + PlistBundleCount = PlistDictChildren (PlistBundle); + for (PlistBundleIndex = 0; PlistBundleIndex < PlistBundleCount; PlistBundleIndex++) { + PlistBundleKey = PlistKeyValue (PlistDictChild (PlistBundle, PlistBundleIndex, &BundleExecutable)); + if (PlistBundleKey == NULL) { + continue; + } + + if (AsciiStrCmp (PlistBundleKey, MKEXT_EXECUTABLE_KEY) == 0) { + if (!PlistIntegerValue (BundleExecutable, &BinOffset, sizeof (BinOffset), TRUE) + || BinOffset == 0 + || BinOffset > MkextSize - sizeof (MKEXT_V2_FILE_ENTRY)) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + if (Decompress) { + FreePool (BinaryOffsetStrings); + } + return EFI_INVALID_PARAMETER; + } + + MkextExecutableEntry = (MKEXT_V2_FILE_ENTRY *) &Buffer[BinOffset]; + BinCompSize = SwapBytes32 (MkextExecutableEntry->CompressedSize); + BinFullSize = SwapBytes32 (MkextExecutableEntry->FullSize); + BinFullSizeAligned = MKEXT_ALIGN (BinFullSize); + + if (OcOverflowTriAddU32 (CurrentOffset, sizeof (MKEXT_V2_FILE_ENTRY), BinFullSizeAligned, &NewOffset)) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + if (Decompress) { + FreePool (BinaryOffsetStrings); + } + return EFI_INVALID_PARAMETER; + } + + if (Decompress) { + if (NewOffset > OutBufferSize) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + FreePool (BinaryOffsetStrings); + return EFI_BUFFER_TOO_SMALL; + } + + MkextOutExecutableEntry = (MKEXT_V2_FILE_ENTRY *) &OutBuffer[CurrentOffset]; + MkextOutExecutableEntry->CompressedSize = 0; + MkextOutExecutableEntry->FullSize = SwapBytes32 (BinFullSize); + + // + // Compressed size == 0 means no compression. + // + if (BinCompSize > 0) { + if (OcOverflowAddU32 (BinOffset, BinCompSize, &Tmp) + || Tmp > MkextSize - sizeof (MKEXT_V2_FILE_ENTRY) + || DecompressZLIB ( + MkextOutExecutableEntry->Data, + BinFullSize, + MkextExecutableEntry->Data, + BinCompSize + ) != BinFullSize) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + FreePool (BinaryOffsetStrings); + return EFI_INVALID_PARAMETER; + } + } else { + if (OcOverflowAddU32 (BinOffset, BinFullSize, &Tmp) + || Tmp > MkextSize - sizeof (MKEXT_V2_FILE_ENTRY)) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + FreePool (BinaryOffsetStrings); + return EFI_INVALID_PARAMETER; + } + CopyMem (MkextOutExecutableEntry->Data, MkextExecutableEntry->Data, BinFullSize); + } + + if (!AsciiUint64ToLowerHex ( + &BinaryOffsetStrings[Index * MKEXT_OFFSET_STR_LEN], + MKEXT_OFFSET_STR_LEN, + CurrentOffset + )) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + FreePool (BinaryOffsetStrings); + return EFI_INVALID_PARAMETER; + } + XmlNodeChangeContent (BundleExecutable, &BinaryOffsetStrings[Index * MKEXT_OFFSET_STR_LEN]); + } + + // + // Move to next bundle. + // + CurrentOffset = NewOffset; + break; + } + } + } + + if (Decompress) { + PlistFullSize = UpdateMkextV2Plist (&MkextHeaderOut->V2, OutBufferSize, PlistXml, CurrentOffset); + } + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + if (Decompress) { + FreePool (BinaryOffsetStrings); + + if (PlistFullSize == 0 + || OcOverflowAddU32 (CurrentOffset, PlistFullSize, OutMkextSize)) { + return EFI_INVALID_PARAMETER; + } + } else { + // + // Account for plist, future plist expansion for each bundle, + // and additional headers for future kext injection. + // + PlistFullSize = SwapBytes32 (MkextHeader->V2.PlistFullSize); + if (OcOverflowAddU32 (CurrentOffset, PlistFullSize, &CurrentOffset) + || OcOverflowMulAddU32 (PlistBundlesCount, PLIST_EXPANSION_SIZE, CurrentOffset, &CurrentOffset) + || OcOverflowMulAddU32 (NumReservedKexts, sizeof (MKEXT_V2_FILE_ENTRY), CurrentOffset, OutMkextSize)) { + return EFI_INVALID_PARAMETER; + } + } + + // + // Unsupported version. + // + } else { + return EFI_UNSUPPORTED; + } + + if (Decompress) { + UpdateMkextLengthChecksum (MkextHeaderOut, *OutMkextSize); + } + return EFI_SUCCESS; +} + +BOOLEAN +MkextCheckCpuType ( + IN UINT8 *Mkext, + IN UINT32 MkextSize, + IN MACH_CPU_TYPE CpuType + ) +{ + MKEXT_HEADER_ANY *MkextHeader; + MACH_CPU_TYPE MkextCpuType; + + ASSERT (Mkext != NULL); + ASSERT (MkextSize > 0); + + if (MkextSize < sizeof (MKEXT_CORE_HEADER) + || !OC_TYPE_ALIGNED (MKEXT_CORE_HEADER, Mkext)) { + return FALSE; + } + + MkextHeader = (MKEXT_HEADER_ANY *) Mkext; + MkextCpuType = SwapBytes32 (MkextHeader->Common.CpuType); + + if (MkextHeader->Common.Magic != MKEXT_INVERT_MAGIC + || MkextHeader->Common.Signature != MKEXT_INVERT_SIGNATURE + || MkextSize != SwapBytes32 (MkextHeader->Common.Length)) { + return FALSE; + } + + return MkextCpuType == CpuType; +} + +EFI_STATUS +MkextContextInit ( + IN OUT MKEXT_CONTEXT *Context, + IN OUT UINT8 *Mkext, + IN UINT32 MkextSize, + IN UINT32 MkextAllocSize + ) +{ + MKEXT_HEADER_ANY *MkextHeader; + UINT32 MkextVersion; + UINT32 MkextHeaderSize; + MACH_CPU_TYPE CpuType; + BOOLEAN Is64Bit; + UINT32 NumKexts; + UINT32 NumMaxKexts; + + UINT32 Tmp; + UINT32 Index; + UINT32 StartingOffset; + UINT32 CurrentOffset; + + UINT8 *PlistBuffer; + XML_DOCUMENT *PlistXml; + UINT32 PlistOffset; + XML_NODE *PlistBundles; + UINT32 PlistBundlesCount; + XML_NODE *PlistBundle; + UINT32 PlistBundleIndex; + UINT32 PlistBundleCount; + CONST CHAR8 *PlistBundleKey; + XML_NODE *BundleExecutable; + UINT32 BinOffset; + + // + // Assumptions: + // Kexts are fully decompressed and aligned to 8 bytes with plist (for v2) at end of mkext. + // Mkext is big-endian per XNU requirements. + // + + ASSERT (Context != NULL); + ASSERT (Mkext != NULL); + ASSERT (MkextSize > 0); + ASSERT (MkextAllocSize >= MkextSize); + + if (MkextSize < sizeof (MKEXT_CORE_HEADER) + || !OC_TYPE_ALIGNED (MKEXT_CORE_HEADER, Mkext)) { + return EFI_INVALID_PARAMETER; + } + + MkextHeader = (MKEXT_HEADER_ANY *) Mkext; + MkextVersion = SwapBytes32 (MkextHeader->Common.Version); + NumKexts = SwapBytes32 (MkextHeader->Common.NumKexts); + CpuType = SwapBytes32 (MkextHeader->Common.CpuType); + + if (MkextHeader->Common.Magic != MKEXT_INVERT_MAGIC + || MkextHeader->Common.Signature != MKEXT_INVERT_SIGNATURE + || MkextSize != SwapBytes32 (MkextHeader->Common.Length)) { + return EFI_INVALID_PARAMETER; + } + + if (CpuType == MachCpuTypeI386) { + Is64Bit = FALSE; + } else if (CpuType == MachCpuTypeX8664) { + Is64Bit = TRUE; + } else { + return EFI_UNSUPPORTED; + } + + // + // Mkext v1. + // + if (MkextVersion == MKEXT_VERSION_V1) { + // + // Validate header and array size. + // + if (OcOverflowMulAddU32 (sizeof (MKEXT_V1_KEXT), NumKexts, sizeof (MKEXT_V1_HEADER), &MkextHeaderSize) + || MkextHeaderSize > MkextSize) { + return EFI_INVALID_PARAMETER; + } + + // + // Calculate available kext slots. This value is assumed to be under the UINT32 max later on. + // + // Below finds the lowest offset to a plist or a binary, which is used to locate + // the end of the usable space allocated for kext slots. + // + StartingOffset = 0; + for (Index = 0; Index < NumKexts; Index++) { + CurrentOffset = SwapBytes32 (MkextHeader->V1.Kexts[Index].Plist.Offset); + if (MkextHeader->V1.Kexts[Index].Plist.CompressedSize != 0) { + return EFI_UNSUPPORTED; + } + if (StartingOffset == 0 || CurrentOffset < StartingOffset) { + StartingOffset = CurrentOffset; + } + + // + // A binary entry of zero size indicates no binary is present. + // + if (MkextHeader->V1.Kexts[Index].Binary.FullSize > 0) { + CurrentOffset = SwapBytes32 (MkextHeader->V1.Kexts[Index].Binary.Offset); + if (MkextHeader->V1.Kexts[Index].Binary.CompressedSize != 0) { + return EFI_UNSUPPORTED; + } + if (CurrentOffset < StartingOffset) { + StartingOffset = CurrentOffset; + } + } + } + + if (StartingOffset < MkextHeaderSize + || StartingOffset > MkextSize) { + return EFI_INVALID_PARAMETER; + } + + Tmp = (StartingOffset - MkextHeaderSize) / sizeof (MKEXT_V1_KEXT); + if (OcOverflowAddU32 (Tmp, NumKexts, &NumMaxKexts) + || NumMaxKexts == MAX_UINT32) { + return EFI_INVALID_PARAMETER; + } + + // + // Mkext v2. + // + } else if (MkextVersion == MKEXT_VERSION_V2) { + if (MkextSize < sizeof (MKEXT_V2_HEADER) + || !ParseMkextV2Plist (&MkextHeader->V2, &PlistBuffer, &PlistXml, &PlistBundles)) { + return EFI_INVALID_PARAMETER; + } + PlistOffset = SwapBytes32 (MkextHeader->V2.PlistOffset); + + // + // Enumerate bundle dicts. + // + PlistBundlesCount = XmlNodeChildren (PlistBundles); + for (Index = 0; Index < PlistBundlesCount; Index++) { + PlistBundle = PlistNodeCast (XmlNodeChild (PlistBundles, Index), PLIST_NODE_TYPE_DICT); + if (PlistBundle == NULL) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + + PlistBundleCount = PlistDictChildren (PlistBundle); + for (PlistBundleIndex = 0; PlistBundleIndex < PlistBundleCount; PlistBundleIndex++) { + PlistBundleKey = PlistKeyValue (PlistDictChild (PlistBundle, PlistBundleIndex, &BundleExecutable)); + if (PlistBundleKey == NULL) { + continue; + } + + if (AsciiStrCmp (PlistBundleKey, MKEXT_EXECUTABLE_KEY) == 0) { + // + // Ensure binary offset is before plist offset. + // + if (!PlistIntegerValue (BundleExecutable, &BinOffset, sizeof (BinOffset), TRUE) + || BinOffset == 0 + || BinOffset >= PlistOffset) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + } + } + } + + // + // Unsupported version. + // + } else { + return EFI_UNSUPPORTED; + } + + ZeroMem (Context, sizeof (*Context)); + Context->Mkext = Mkext; + Context->MkextSize = MkextSize; + Context->MkextHeader = MkextHeader; + Context->MkextAllocSize = MkextAllocSize; + Context->MkextVersion = MkextVersion; + Context->Is64Bit = Is64Bit; + Context->NumKexts = NumKexts; + + if (MkextVersion == MKEXT_VERSION_V1) { + Context->NumMaxKexts = NumMaxKexts; + } else if (MkextVersion == MKEXT_VERSION_V2) { + Context->MkextInfoOffset = PlistOffset; + Context->MkextInfo = PlistBuffer; + Context->MkextInfoDocument = PlistXml; + Context->MkextKexts = PlistBundles; + } + + return EFI_SUCCESS; +} + +VOID +MkextContextFree ( + IN OUT MKEXT_CONTEXT *Context + ) +{ + ASSERT (Context != NULL); + + if (Context->MkextInfoDocument != NULL) { + XmlDocumentFree (Context->MkextInfoDocument); + } + if (Context->MkextInfo != NULL) { + FreePool (Context->MkextInfo); + } + + ZeroMem (Context, sizeof (*Context)); +} + +EFI_STATUS +MkextReserveKextSize ( + IN OUT UINT32 *ReservedInfoSize, + IN OUT UINT32 *ReservedExeSize, + IN UINT32 InfoPlistSize, + IN UINT8 *Executable, + IN UINT32 ExecutableSize OPTIONAL + ) +{ + OC_MACHO_CONTEXT Context; + + ASSERT (ReservedInfoSize != NULL); + ASSERT (ReservedExeSize != NULL); + + InfoPlistSize = MACHO_ALIGN (InfoPlistSize); + + if (Executable != NULL) { + ASSERT (ExecutableSize > 0); + if (!MachoInitializeContext (&Context, Executable, ExecutableSize, 0)) { + return EFI_INVALID_PARAMETER; + } + + ExecutableSize = MachoGetVmSize64 (&Context); + if (ExecutableSize == 0) { + return EFI_INVALID_PARAMETER; + } + } + + if (OcOverflowAddU32 (*ReservedInfoSize, InfoPlistSize, &InfoPlistSize) + || OcOverflowAddU32 (*ReservedExeSize, ExecutableSize, &ExecutableSize)) { + return EFI_INVALID_PARAMETER; + } + + *ReservedInfoSize = InfoPlistSize; + *ReservedExeSize = ExecutableSize; + + return EFI_SUCCESS; +} + +EFI_STATUS +MkextInjectKext ( + IN OUT MKEXT_CONTEXT *Context, + IN CONST CHAR8 *BundlePath, + IN CONST CHAR8 *InfoPlist, + IN UINT32 InfoPlistSize, + IN UINT8 *Executable OPTIONAL, + IN UINT32 ExecutableSize OPTIONAL + ) +{ + UINT32 MkextNewSize; + UINT32 PlistOffset; + UINT32 BinOffset; + UINT32 InfoPlistSizeAligned; + UINT32 ExecutableSizeAligned; + + CHAR8 *PlistBuffer; + CHAR8 *PlistExported; + UINT32 PlistExportedSize; + XML_DOCUMENT *PlistXml; + XML_NODE *PlistRoot; + BOOLEAN PlistFailed; + + CONST CHAR8 *TmpKeyValue; + UINT32 FieldCount; + UINT32 FieldIndex; + + CHAR8 ExecutableSourceAddrStr[24]; + MKEXT_V2_FILE_ENTRY *MkextExecutableEntry; + + ASSERT (Context != NULL); + ASSERT (BundlePath != NULL); + ASSERT (InfoPlist != NULL); + ASSERT (InfoPlistSize > 0); + + // + // Mkext v1. + // + if (Context->MkextVersion == MKEXT_VERSION_V1) { + if (Context->NumKexts >= Context->NumMaxKexts) { + return EFI_BUFFER_TOO_SMALL; + } + + InfoPlistSizeAligned = MKEXT_ALIGN (InfoPlistSize); + if (InfoPlistSizeAligned < InfoPlistSize) { + return EFI_INVALID_PARAMETER; + } + + // + // Plist will be placed at end of mkext. + // + PlistOffset = Context->MkextSize; + if (OcOverflowAddU32 (PlistOffset, InfoPlistSizeAligned, &MkextNewSize)) { + return EFI_INVALID_PARAMETER; + } + if (MkextNewSize > Context->MkextAllocSize) { + return EFI_BUFFER_TOO_SMALL; + } + + // + // Executable, if present, will be placed right after plist. + // + if (Executable != NULL) { + ASSERT (ExecutableSize > 0); + + BinOffset = MkextNewSize; + if (!ParseKextBinary (&Executable, &ExecutableSize, Context->Is64Bit)) { + return EFI_INVALID_PARAMETER; + } + + ExecutableSizeAligned = MKEXT_ALIGN (ExecutableSize); + if (ExecutableSizeAligned < ExecutableSize + || OcOverflowAddU32 (BinOffset, ExecutableSizeAligned, &MkextNewSize)) { + return EFI_INVALID_PARAMETER; + } + if (MkextNewSize > Context->MkextAllocSize) { + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (&Context->Mkext[BinOffset], Executable, ExecutableSize); + Context->MkextHeader->V1.Kexts[Context->NumKexts].Binary.Offset = SwapBytes32 (BinOffset); + Context->MkextHeader->V1.Kexts[Context->NumKexts].Binary.CompressedSize = 0; + Context->MkextHeader->V1.Kexts[Context->NumKexts].Binary.FullSize = SwapBytes32 (ExecutableSize); + Context->MkextHeader->V1.Kexts[Context->NumKexts].Binary.ModifiedSeconds = 0; + } + + CopyMem (&Context->Mkext[PlistOffset], InfoPlist, InfoPlistSize); + Context->MkextHeader->V1.Kexts[Context->NumKexts].Plist.Offset = SwapBytes32 (PlistOffset); + Context->MkextHeader->V1.Kexts[Context->NumKexts].Plist.CompressedSize = 0; + Context->MkextHeader->V1.Kexts[Context->NumKexts].Plist.FullSize = SwapBytes32 (InfoPlistSize); + Context->MkextHeader->V1.Kexts[Context->NumKexts].Plist.ModifiedSeconds = 0; + + // + // Assumption: + // NumKexts is checked to be under MaxNumKexts, which is assumed to be under + // the max value of UINT32 during context creation. This operation thus cannot overflow. + // + Context->MkextSize = MkextNewSize; + Context->NumKexts++; + + // + // Mkext v2. + // + } else if (Context->MkextVersion == MKEXT_VERSION_V2) { + PlistBuffer = AllocateCopyPool (InfoPlistSize, InfoPlist); + if (PlistBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PlistXml = XmlDocumentParse (PlistBuffer, InfoPlistSize, FALSE); + if (PlistXml == NULL) { + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + + PlistRoot = PlistNodeCast (PlistDocumentRoot (PlistXml), PLIST_NODE_TYPE_DICT); + if (PlistRoot == NULL) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + + // + // We are not supposed to check for this, it is XNU responsibility, which reliably panics. + // However, to avoid certain users making this kind of mistake, we still provide some + // code in debug mode to diagnose it. + // + DEBUG_CODE_BEGIN (); + if (Executable == NULL) { + FieldCount = PlistDictChildren (PlistRoot); + for (FieldIndex = 0; FieldIndex < FieldCount; ++FieldIndex) { + TmpKeyValue = PlistKeyValue (PlistDictChild (PlistRoot, FieldIndex, NULL)); + if (TmpKeyValue == NULL) { + continue; + } + + if (AsciiStrCmp (TmpKeyValue, INFO_BUNDLE_EXECUTABLE_KEY) == 0) { + DEBUG ((DEBUG_ERROR, "OCK: Plist-only kext has %a key\n", INFO_BUNDLE_EXECUTABLE_KEY)); + ASSERT (FALSE); + CpuDeadLoop (); + } + } + } + DEBUG_CODE_END (); + + // + // Executable, if present, will be placed at end of mkext and plist will be moved further out. + // + PlistFailed = FALSE; + if (Executable != NULL) { + ASSERT (ExecutableSize > 0); + + BinOffset = Context->MkextInfoOffset; + if (!ParseKextBinary (&Executable, &ExecutableSize, Context->Is64Bit)) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + ExecutableSizeAligned = MKEXT_ALIGN (ExecutableSize); + if (ExecutableSizeAligned < ExecutableSize + || OcOverflowTriAddU32 (BinOffset, sizeof (MKEXT_V2_FILE_ENTRY), ExecutableSizeAligned, &PlistOffset)) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_INVALID_PARAMETER; + } + + if (PlistOffset >= Context->MkextAllocSize) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_BUFFER_TOO_SMALL; + } + + PlistFailed |= !AsciiUint64ToLowerHex (ExecutableSourceAddrStr, sizeof (ExecutableSourceAddrStr), BinOffset); + PlistFailed |= XmlNodeAppend (PlistRoot, "key", NULL, MKEXT_EXECUTABLE_KEY) == NULL; + PlistFailed |= XmlNodeAppend (PlistRoot, "integer", MKEXT_INFO_INTEGER_ATTRIBUTES, ExecutableSourceAddrStr) == NULL; + } + + // + // Add bundle path. + // + PlistFailed |= XmlNodeAppend (PlistRoot, "key", NULL, MKEXT_BUNDLE_PATH_KEY) == NULL; + PlistFailed |= XmlNodeAppend (PlistRoot, "string", NULL, BundlePath) == NULL; + if (PlistFailed) { + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + return EFI_OUT_OF_RESOURCES; + } + + // + // Strip down Info.plist for appending to primary plist. + // + PlistExported = XmlDocumentExport (PlistXml, &PlistExportedSize, 2, FALSE); + XmlDocumentFree (PlistXml); + FreePool (PlistBuffer); + + if (XmlNodeAppend (Context->MkextKexts, "dict", NULL, PlistExported) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Context->MkextInfoOffset = PlistOffset; + + if (Executable != NULL) { + MkextExecutableEntry = (MKEXT_V2_FILE_ENTRY *) &Context->Mkext[BinOffset]; + MkextExecutableEntry->CompressedSize = 0; + MkextExecutableEntry->FullSize = SwapBytes32 (ExecutableSize); + CopyMem (MkextExecutableEntry->Data, Executable, ExecutableSize); + } + + // + // Unsupported version. + // + } else { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +MkextInjectPatchComplete ( + IN OUT MKEXT_CONTEXT *Context + ) +{ + UINT32 MkextPlistSize; + + ASSERT (Context != NULL); + + // + // Mkext v1. + // + if (Context->MkextVersion == MKEXT_VERSION_V1) { + Context->MkextHeader->Common.NumKexts = SwapBytes32 (Context->NumKexts); + + // + // Mkext v2. + // + } else if (Context->MkextVersion == MKEXT_VERSION_V2) { + MkextPlistSize = UpdateMkextV2Plist ( + &Context->MkextHeader->V2, + Context->MkextAllocSize, + Context->MkextInfoDocument, + Context->MkextInfoOffset + ); + Context->MkextSize = Context->MkextInfoOffset + MkextPlistSize; + + // + // Unsupported version. + // + } else { + return EFI_UNSUPPORTED; + } + + UpdateMkextLengthChecksum (Context->MkextHeader, Context->MkextSize); + return EFI_SUCCESS; +} diff --git a/Library/OcAppleKernelLib/MkextReader.c b/Library/OcAppleKernelLib/MkextReader.c new file mode 100644 index 00000000..faeea81c --- /dev/null +++ b/Library/OcAppleKernelLib/MkextReader.c @@ -0,0 +1,132 @@ +/** @file + Mkext support. + + Copyright (c) 2020, 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Pick a reasonable maximum to fit. +// +#define MKEXT_HEADER_SIZE (EFI_PAGE_SIZE * 2) + +EFI_STATUS +ReadAppleMkext ( + IN EFI_FILE_PROTOCOL *File, + IN MACH_CPU_TYPE CpuType, + OUT UINT8 **Mkext, + OUT UINT32 *MkextSize, + OUT UINT32 *AllocatedSize, + IN UINT32 ReservedSize, + IN UINT32 NumReservedKexts + ) +{ + EFI_STATUS Status; + UINT32 Offset; + UINT8 *TmpMkext; + UINT32 TmpMkextSize; + UINT32 TmpMkextFileSize; + + ASSERT (File != NULL); + ASSERT (Mkext != NULL); + ASSERT (MkextSize != NULL); + ASSERT (AllocatedSize != NULL); + + // + // Read enough to get fat binary header if present. + // + TmpMkextSize = MKEXT_HEADER_SIZE; + Status = GetFileSize (File, &TmpMkextFileSize); + if (TmpMkextSize >= TmpMkextFileSize) { + return EFI_INVALID_PARAMETER; + } + + TmpMkext = AllocatePool (TmpMkextSize); + if (TmpMkext == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = GetFileData (File, 0, TmpMkextSize, TmpMkext); + if (EFI_ERROR (Status)) { + FreePool (TmpMkext); + return Status; + } + Status = FatGetArchitectureOffset (TmpMkext, TmpMkextSize, TmpMkextFileSize, CpuType, &Offset, &TmpMkextSize); + FreePool (TmpMkext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Read target slice of mkext. + // + TmpMkext = AllocatePool (TmpMkextSize); + if (TmpMkext == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Status = GetFileData (File, Offset, TmpMkextSize, TmpMkext); + if (EFI_ERROR (Status)) { + FreePool (TmpMkext); + return Status; + } + + // + // Verify mkext arch. + // + if (!MkextCheckCpuType (TmpMkext, TmpMkextSize, CpuType)) { + FreePool (TmpMkext); + return EFI_UNSUPPORTED; + } + DEBUG ((DEBUG_INFO, "OCAK: Got mkext of %u bytes with 0x%X arch\n", TmpMkextSize, CpuType)); + + // + // Calculate size of decompressed mkext. + // + *AllocatedSize = 0; + Status = MkextDecompress (TmpMkext, TmpMkextSize, NumReservedKexts, NULL, 0, AllocatedSize); + if (EFI_ERROR (Status)) { + FreePool (TmpMkext); + return Status; + } + if (OcOverflowAddU32 (*AllocatedSize, ReservedSize, AllocatedSize)) { + FreePool (TmpMkext); + return EFI_INVALID_PARAMETER; + } + + *Mkext = AllocatePool (*AllocatedSize); + if (*Mkext == NULL) { + FreePool (TmpMkext); + return EFI_OUT_OF_RESOURCES; + } + + // + // Decompress mkext into final buffer. + // + Status = MkextDecompress (TmpMkext, TmpMkextSize, NumReservedKexts, *Mkext, *AllocatedSize, MkextSize); + FreePool (TmpMkext); + + if (EFI_ERROR (Status)) { + FreePool (*Mkext); + } + + return Status; +} diff --git a/Library/OcAppleKernelLib/OcAppleKernelLib.inf b/Library/OcAppleKernelLib/OcAppleKernelLib.inf index 26b1d209..05d8f2d3 100644 --- a/Library/OcAppleKernelLib/OcAppleKernelLib.inf +++ b/Library/OcAppleKernelLib/OcAppleKernelLib.inf @@ -39,6 +39,8 @@ PrelinkedKext.c Vtables.c CachelessContext.c + MkextContext.c + MkextReader.c [Packages] MdePkg/MdePkg.dec diff --git a/Library/OcAppleKernelLib/PrelinkedContext.c b/Library/OcAppleKernelLib/PrelinkedContext.c index 41385039..aa999766 100644 --- a/Library/OcAppleKernelLib/PrelinkedContext.c +++ b/Library/OcAppleKernelLib/PrelinkedContext.c @@ -762,7 +762,7 @@ PrelinkedReserveKextSize ( // // For new fields. // - if (OcOverflowAddU32 (InfoPlistSize, 512, &InfoPlistSize)) { + if (OcOverflowAddU32 (InfoPlistSize, PLIST_EXPANSION_SIZE, &InfoPlistSize)) { return EFI_INVALID_PARAMETER; } diff --git a/Library/OcMachoLib/CxxSymbols.c b/Library/OcMachoLib/CxxSymbols.c index f929fd45..7281b584 100644 --- a/Library/OcMachoLib/CxxSymbols.c +++ b/Library/OcMachoLib/CxxSymbols.c @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include +#include #include diff --git a/Library/OcMachoLib/Fat.c b/Library/OcMachoLib/Fat.c new file mode 100644 index 00000000..a38aba5b --- /dev/null +++ b/Library/OcMachoLib/Fat.c @@ -0,0 +1,151 @@ +/** @file + Copyright (C) 2020, 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 + +#include +#include + +#include +#include +#include +#include +#include + +#include "OcMachoLibInternal.h" + +EFI_STATUS +FatGetArchitectureOffset ( + IN CONST UINT8 *Buffer, + IN UINT32 BufferSize, + IN UINT32 FullSize, + IN MACH_CPU_TYPE CpuType, + OUT UINT32 *FatOffset, + OUT UINT32 *FatSize + ) +{ + BOOLEAN SwapBytes; + MACH_FAT_HEADER *FatHeader; + UINT32 NumberOfFatArch; + UINT32 Offset; + MACH_CPU_TYPE TmpCpuType; + UINT32 TmpSize; + UINT32 Index; + UINT32 Size; + + ASSERT (Buffer != NULL); + ASSERT (BufferSize > 0); + ASSERT (FullSize > 0); + ASSERT (FatOffset != NULL); + ASSERT (FatSize != NULL); + + if (BufferSize < sizeof (MACH_FAT_HEADER) + || !OC_TYPE_ALIGNED (MACH_FAT_HEADER, Buffer)) { + return EFI_INVALID_PARAMETER; + } + + FatHeader = (MACH_FAT_HEADER*) Buffer; + if (FatHeader->Signature != MACH_FAT_BINARY_INVERT_SIGNATURE + && FatHeader->Signature != MACH_FAT_BINARY_SIGNATURE) { + // + // Non-fat binary. + // + *FatOffset = 0; + *FatSize = FullSize; + return EFI_SUCCESS; + } + + SwapBytes = FatHeader->Signature == MACH_FAT_BINARY_INVERT_SIGNATURE; + NumberOfFatArch = FatHeader->NumberOfFatArch; + if (SwapBytes) { + NumberOfFatArch = SwapBytes32 (NumberOfFatArch); + } + + if (OcOverflowMulAddU32 (NumberOfFatArch, sizeof (MACH_FAT_ARCH), sizeof (MACH_FAT_HEADER), &TmpSize) + || TmpSize > BufferSize) { + return EFI_INVALID_PARAMETER; + } + + // + // TODO: extend the interface to support subtypes (i.e. MachCpuSubtypeX8664H) some day. + // + for (Index = 0; Index < NumberOfFatArch; ++Index) { + TmpCpuType = FatHeader->FatArch[Index].CpuType; + if (SwapBytes) { + TmpCpuType = SwapBytes32 (TmpCpuType); + } + if (TmpCpuType == CpuType) { + Offset = FatHeader->FatArch[Index].Offset; + Size = FatHeader->FatArch[Index].Size; + if (SwapBytes) { + Offset = SwapBytes32 (Offset); + Size = SwapBytes32 (Size); + } + + if (Offset == 0 + || OcOverflowAddU32 (Offset, Size, &TmpSize) + || TmpSize > FullSize) { + return EFI_INVALID_PARAMETER; + } + + *FatOffset = Offset; + *FatSize = Size; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +FatFilterArchitectureByType ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize, + IN MACH_CPU_TYPE CpuType + ) +{ + EFI_STATUS Status; + UINT32 FatOffset; + UINT32 FatSize; + + ASSERT (FileData != NULL); + ASSERT (FileSize != NULL); + ASSERT (*FileSize > 0); + + Status = FatGetArchitectureOffset (*FileData, *FileSize, *FileSize, CpuType, &FatOffset, &FatSize); + if (EFI_ERROR (Status)) { + return Status; + } + + *FileData += FatOffset; + *FileSize = FatSize; + + return EFI_SUCCESS; +} + +EFI_STATUS +FatFilterArchitecture32 ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize + ) +{ + return FatFilterArchitectureByType (FileData, FileSize, MachCpuTypeX86); +} + +EFI_STATUS +FatFilterArchitecture64 ( + IN OUT UINT8 **FileData, + IN OUT UINT32 *FileSize + ) +{ + return FatFilterArchitectureByType (FileData, FileSize, MachCpuTypeX8664); +} diff --git a/Library/OcMachoLib/Header.c b/Library/OcMachoLib/Header.c index bd793286..8fef7544 100644 --- a/Library/OcMachoLib/Header.c +++ b/Library/OcMachoLib/Header.c @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include +#include #include #include @@ -96,84 +96,6 @@ MachoGetVmSize64 ( return (UINT32) VmSize; } -/** - Moves file pointer and size to point to x86_64 slice in case - FAT Mach-O is used. - - @param[in,out] FileData Pointer to pointer of the file's data. - @param[in,out] FileSize Pointer to file size of FileData. - - @return FALSE is not valid FAT image. -**/ -STATIC -BOOLEAN -MachoFilterFatArchitecture64 ( - IN OUT UINT8 **FileData, - IN OUT UINT32 *FileSize - ) -{ - BOOLEAN SwapBytes; - MACH_FAT_HEADER *FatHeader; - UINT32 NumberOfFatArch; - UINT32 Offset; - MACH_CPU_TYPE CpuType; - UINT32 TmpSize; - UINT32 Index; - UINT32 Size; - - if (*FileSize < sizeof (MACH_FAT_HEADER) - || !OC_TYPE_ALIGNED (MACH_FAT_HEADER, *FileData)) { - return FALSE; - } - FatHeader = (MACH_FAT_HEADER *)* FileData; - if (FatHeader->Signature != MACH_FAT_BINARY_INVERT_SIGNATURE - && FatHeader->Signature != MACH_FAT_BINARY_SIGNATURE) { - return FALSE; - } - - SwapBytes = FatHeader->Signature == MACH_FAT_BINARY_INVERT_SIGNATURE; - NumberOfFatArch = FatHeader->NumberOfFatArch; - if (SwapBytes) { - NumberOfFatArch = SwapBytes32 (NumberOfFatArch); - } - - if (OcOverflowMulAddU32 (NumberOfFatArch, sizeof (MACH_FAT_ARCH), sizeof (MACH_FAT_HEADER), &TmpSize) - || TmpSize > *FileSize) { - return FALSE; - } - - // - // TODO: extend the interface to support MachCpuSubtypeX8664H some day. - // - for (Index = 0; Index < NumberOfFatArch; ++Index) { - CpuType = FatHeader->FatArch[Index].CpuType; - if (SwapBytes) { - CpuType = SwapBytes32 (CpuType); - } - if (CpuType == MachCpuTypeX8664) { - Offset = FatHeader->FatArch[Index].Offset; - Size = FatHeader->FatArch[Index].Size; - if (SwapBytes) { - Offset = SwapBytes32 (Offset); - Size = SwapBytes32 (Size); - } - - if (Offset == 0 - || OcOverflowAddU32 (Offset, Size, &TmpSize) - || TmpSize > *FileSize) { - return FALSE; - } - - *FileData = *FileData + Offset; - *FileSize = Size; - - return TRUE; - } - } - - return FALSE; -} - /** Initializes a Mach-O Context. @@ -191,6 +113,7 @@ MachoInitializeContext ( IN UINT32 ContainerOffset ) { + EFI_STATUS Status; MACH_HEADER_64 *MachHeader; UINTN TopOfFile; UINTN TopOfCommands; @@ -207,7 +130,10 @@ MachoInitializeContext ( TopOfFile = ((UINTN)FileData + FileSize); ASSERT (TopOfFile > (UINTN)FileData); - MachoFilterFatArchitecture64 ((UINT8 **) &FileData, &FileSize); + Status = FatFilterArchitecture64 ((UINT8 **) &FileData, &FileSize); + if (EFI_ERROR (Status)) { + return FALSE; + } if (FileSize < sizeof (*MachHeader) || !OC_TYPE_ALIGNED (MACH_HEADER_64, FileData)) { diff --git a/Library/OcMachoLib/OcMachoLib.inf b/Library/OcMachoLib/OcMachoLib.inf index 424ec392..186b6c28 100644 --- a/Library/OcMachoLib/OcMachoLib.inf +++ b/Library/OcMachoLib/OcMachoLib.inf @@ -30,6 +30,7 @@ [Sources] CxxSymbols.c + Fat.c Header.c OcMachoLibInternal.h Relocations.c diff --git a/Library/OcMachoLib/Relocations.c b/Library/OcMachoLib/Relocations.c index 1b71f3a8..749c6758 100644 --- a/Library/OcMachoLib/Relocations.c +++ b/Library/OcMachoLib/Relocations.c @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include +#include #include diff --git a/Library/OcMachoLib/Symbols.c b/Library/OcMachoLib/Symbols.c index d5695cb0..8d57f544 100644 --- a/Library/OcMachoLib/Symbols.c +++ b/Library/OcMachoLib/Symbols.c @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ -#include +#include #include diff --git a/Platform/OpenCore/OpenCoreKernel.c b/Platform/OpenCore/OpenCoreKernel.c index cd7272f7..73a66a2d 100644 --- a/Platform/OpenCore/OpenCoreKernel.c +++ b/Platform/OpenCore/OpenCoreKernel.c @@ -36,6 +36,15 @@ STATIC UINT32 mOcDarwinVersion; STATIC CACHELESS_CONTEXT mOcCachelessContext; STATIC BOOLEAN mOcCachelessInProgress; +// +// Kernel cache types. +// +typedef enum KERNEL_CACHE_TYPE_ { + CacheTypeCacheless, + CacheTypeMkext, + CacheTypePrelinked +} KERNEL_CACHE_TYPE; + STATIC UINT32 OcParseDarwinVersion ( @@ -178,10 +187,12 @@ OcKernelReadDarwinVersion ( STATIC EFI_STATUS OcKernelLoadKextsAndReserve ( - IN OC_STORAGE_CONTEXT *Storage, - IN OC_GLOBAL_CONFIG *Config, - OUT UINT32 *ReservedExeSize, - OUT UINT32 *ReservedInfoSize + IN OC_STORAGE_CONTEXT *Storage, + IN OC_GLOBAL_CONFIG *Config, + IN KERNEL_CACHE_TYPE CacheType, + OUT UINT32 *ReservedExeSize, + OUT UINT32 *ReservedInfoSize, + OUT UINT32 *NumReservedKexts ) { EFI_STATUS Status; @@ -195,6 +206,7 @@ OcKernelLoadKextsAndReserve ( *ReservedInfoSize = PRELINK_INFO_RESERVE_SIZE; *ReservedExeSize = 0; + *NumReservedKexts = 0; for (Index = 0; Index < Config->Kernel.Add.Count; ++Index) { Kext = Config->Kernel.Add.Values[Index]; @@ -299,13 +311,24 @@ OcKernelLoadKextsAndReserve ( } } - Status = PrelinkedReserveKextSize ( - ReservedInfoSize, - ReservedExeSize, - Kext->PlistDataSize, - Kext->ImageData, - Kext->ImageDataSize - ); + if (CacheType == CacheTypeCacheless || CacheType == CacheTypeMkext) { + Status = MkextReserveKextSize ( + ReservedInfoSize, + ReservedExeSize, + Kext->PlistDataSize, + Kext->ImageData, + Kext->ImageDataSize + ); + } else if (CacheType == CacheTypePrelinked) { + Status = PrelinkedReserveKextSize ( + ReservedInfoSize, + ReservedExeSize, + Kext->PlistDataSize, + Kext->ImageData, + Kext->ImageDataSize + ); + } + if (EFI_ERROR (Status)) { DEBUG (( DEBUG_ERROR, @@ -318,11 +341,15 @@ OcKernelLoadKextsAndReserve ( Kext->PlistData = NULL; continue; } + + (*NumReservedKexts)++; } - if (*ReservedExeSize > PRELINKED_KEXTS_MAX_SIZE - || *ReservedInfoSize + *ReservedExeSize < *ReservedExeSize) { - return EFI_UNSUPPORTED; + if (CacheType == CacheTypePrelinked) { + if (*ReservedExeSize > PRELINKED_KEXTS_MAX_SIZE + || *ReservedInfoSize + *ReservedExeSize < *ReservedExeSize) { + return EFI_UNSUPPORTED; + } } DEBUG (( @@ -727,6 +754,92 @@ OcKernelProcessPrelinked ( return Status; } +STATIC +EFI_STATUS +OcKernelProcessMkext ( + IN OC_GLOBAL_CONFIG *Config, + IN UINT32 DarwinVersion, + IN OUT UINT8 *Mkext, + IN OUT UINT32 *MkextSize, + IN UINT32 AllocatedSize + ) +{ + EFI_STATUS Status; + MKEXT_CONTEXT Context; + CHAR8 *BundlePath; + CHAR8 *Comment; + UINT32 Index; + CHAR8 FullPath[OC_STORAGE_SAFE_PATH_MAX]; + OC_KERNEL_ADD_ENTRY *Kext; + UINT32 MaxKernel; + UINT32 MinKernel; + + Status = MkextContextInit (&Context, Mkext, *MkextSize, AllocatedSize); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < Config->Kernel.Add.Count; ++Index) { + Kext = Config->Kernel.Add.Values[Index]; + + if (!Kext->Enabled || Kext->PlistDataSize == 0) { + continue; + } + + BundlePath = OC_BLOB_GET (&Kext->BundlePath); + Comment = OC_BLOB_GET (&Kext->Comment); + MaxKernel = OcParseDarwinVersion (OC_BLOB_GET (&Kext->MaxKernel)); + MinKernel = OcParseDarwinVersion (OC_BLOB_GET (&Kext->MinKernel)); + + if (!OcMatchDarwinVersion (DarwinVersion, MinKernel, MaxKernel)) { + DEBUG (( + DEBUG_INFO, + "OC: Mkext injection skips %a (%a) kext at %u due to version %u <= %u <= %u\n", + BundlePath, + Comment, + Index, + MinKernel, + DarwinVersion, + MaxKernel + )); + continue; + } + + Status = OcAsciiSafeSPrint (FullPath, sizeof (FullPath), "/Library/Extensions/%a", BundlePath); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "OC: Failed to fit kext path /Library/Extensions/%a", BundlePath)); + continue; + } + + Status = MkextInjectKext ( + &Context, + FullPath, + Kext->PlistData, + Kext->PlistDataSize, + Kext->ImageData, + Kext->ImageDataSize + ); + + DEBUG (( + EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, + "OC: Mkext injection %a (%a) - %r\n", + BundlePath, + Comment, + Status + )); + } + + Status = MkextInjectPatchComplete (&Context); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "OC: Mkext insertion error - %r\n", Status)); + } + + *MkextSize = Context.MkextSize; + + MkextContextFree (&Context); + return Status; +} + STATIC EFI_STATUS OcKernelInitCacheless ( @@ -819,6 +932,7 @@ OcKernelFileOpen ( EFI_TIME ModificationTime; UINT32 ReservedInfoSize; UINT32 ReservedExeSize; + UINT32 NumReservedKexts; UINT32 LinkedExpansion; UINT32 ReservedFullSize; @@ -866,8 +980,10 @@ OcKernelFileOpen ( OcKernelLoadKextsAndReserve ( mOcStorage, mOcConfiguration, + CacheTypePrelinked, &ReservedExeSize, - &ReservedInfoSize + &ReservedInfoSize, + &NumReservedKexts ); LinkedExpansion = KcGetSegmentFixupChainsSize (ReservedExeSize); @@ -956,6 +1072,68 @@ OcKernelFileOpen ( } } + if (OpenMode == EFI_FILE_MODE_READ + && OcStriStr (FileName, L"Extensions.mkext") != NULL) { + + OcKernelLoadKextsAndReserve ( + mOcStorage, + mOcConfiguration, + CacheTypeMkext, + &ReservedExeSize, + &ReservedInfoSize, + &NumReservedKexts + ); + + Result = OcOverflowAddU32 ( + ReservedInfoSize, + ReservedExeSize, + &ReservedFullSize + ); + if (Result) { + return EFI_UNSUPPORTED; + } + + DEBUG ((DEBUG_INFO, "OC: Trying mkext hook on %s\n", FileName)); + Status = ReadAppleMkext ( + *NewHandle, + MachCpuTypeX8664, + &Kernel, + &KernelSize, + &AllocatedSize, + ReservedFullSize, + NumReservedKexts + ); + DEBUG ((DEBUG_INFO, "OC: Result of mkext hook on %s is %r\n", FileName, Status)); + + if (!EFI_ERROR (Status)) { + // + // Process mkext. + // + Status = OcKernelProcessMkext (mOcConfiguration, mOcDarwinVersion, Kernel, &KernelSize, AllocatedSize); + DEBUG ((DEBUG_INFO, "OC: Mkext status - %r\n", Status)); + if (!EFI_ERROR (Status)) { + Status = GetFileModificationTime (*NewHandle, &ModificationTime); + if (EFI_ERROR (Status)) { + ZeroMem (&ModificationTime, sizeof (ModificationTime)); + } + + (*NewHandle)->Close(*NewHandle); + + Status = CreateVirtualFileFileNameCopy (FileName, Kernel, KernelSize, &ModificationTime, &VirtualFileHandle); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "OC: Failed to virtualise mkext file (%a) - %r\n", FileName, Status)); + FreePool (Kernel); + return EFI_OUT_OF_RESOURCES; + } + + *NewHandle = VirtualFileHandle; + return EFI_SUCCESS; + } else { + FreePool (Kernel); + } + } + } + // // Hook /S/L/E for cacheless boots. // @@ -973,8 +1151,10 @@ OcKernelFileOpen ( OcKernelLoadKextsAndReserve ( mOcStorage, mOcConfiguration, + CacheTypeCacheless, &ReservedExeSize, - &ReservedInfoSize + &ReservedInfoSize, + &NumReservedKexts ); // diff --git a/User/Makefile b/User/Makefile index 8de5fee9..1e78fffd 100644 --- a/User/Makefile +++ b/User/Makefile @@ -136,7 +136,7 @@ ifneq ($(STANDALONE),1) # # OcMachoLib targets. # - OBJS += CxxSymbols.o Header.o Symbols.o Relocations.o + OBJS += CxxSymbols.o Fat.o Header.o Symbols.o Relocations.o # # OcAppleKeysLib targets. #