/** @file
  Copyright (C) 2019, vit9696. All rights reserved.

  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.
**/

#ifndef OC_APPLE_KERNEL_LIB_H
#define OC_APPLE_KERNEL_LIB_H

#include <IndustryStandard/AppleMkext.h>
#include <Library/OcCpuLib.h>
#include <Library/OcMachoLib.h>
#include <Library/OcXmlLib.h>
#include <Protocol/SimpleFileSystem.h>

#define PRELINK_KERNEL_IDENTIFIER      "__kernel__"
#define PRELINK_KPI_IDENTIFIER_PREFIX  "com.apple.kpi."

#define PRELINK_INFO_SEGMENT          "__PRELINK_INFO"
#define PRELINK_INFO_SECTION          "__info"
#define PRELINK_TEXT_SEGMENT          "__PRELINK_TEXT"
#define PRELINK_TEXT_SECTION          "__text"
#define PRELINK_STATE_SEGMENT         "__PRELINK_STATE"
#define PRELINK_STATE_SECTION_KERNEL  "__kernel"
#define PRELINK_STATE_SECTION_KEXTS   "__kexts"

#define PRELINK_INFO_DICTIONARY_KEY                "_PrelinkInfoDictionary"
#define PRELINK_INFO_KMOD_INFO_KEY                 "_PrelinkKmodInfo"
#define PRELINK_INFO_BUNDLE_PATH_KEY               "_PrelinkBundlePath"
#define PRELINK_INFO_EXECUTABLE_RELATIVE_PATH_KEY  "_PrelinkExecutableRelativePath"
#define PRELINK_INFO_EXECUTABLE_LOAD_ADDR_KEY      "_PrelinkExecutableLoadAddr"
#define PRELINK_INFO_EXECUTABLE_SOURCE_ADDR_KEY    "_PrelinkExecutableSourceAddr"
#define PRELINK_INFO_EXECUTABLE_SIZE_KEY           "_PrelinkExecutableSize"
#define PRELINK_INFO_LINK_STATE_ADDR_KEY           "_PrelinkLinkState"
#define PRELINK_INFO_LINK_STATE_SIZE_KEY           "_PrelinkLinkStateSize"

#define INFO_BUNDLE_IDENTIFIER_KEY          "CFBundleIdentifier"
#define INFO_BUNDLE_EXECUTABLE_KEY          "CFBundleExecutable"
#define INFO_BUNDLE_LIBRARIES_KEY           "OSBundleLibraries"
#define INFO_BUNDLE_LIBRARIES_64_KEY        "OSBundleLibraries_x86_64"
#define INFO_BUNDLE_VERSION_KEY             "CFBundleVersion"
#define INFO_BUNDLE_COMPATIBLE_VERSION_KEY  "OSBundleCompatibleVersion"
#define INFO_BUNDLE_OS_BUNDLE_REQUIRED_KEY  "OSBundleRequired"

#define OS_BUNDLE_REQUIRED_ROOT       "Root"
#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"
#define KC_TEXT_SEGMENT           "__TEXT"
#define KC_LINKEDIT_SEGMENT       "__LINKEDIT"
#define KC_MOSCOW_SEGMENT         "__MOSCOW101"

//
// Maximum allowed size of kext bundle version (CFBundleVersion) string.
//
#define MAX_INFO_BUNDLE_VERSION_KEY_SIZE  32

//
// Kernel cache types.
//
typedef enum KERNEL_CACHE_TYPE_ {
  CacheTypeNone,
  CacheTypeCacheless,
  CacheTypeMkext,
  CacheTypePrelinked
} KERNEL_CACHE_TYPE;

//
// Macro to print kernel cache type.
//
#define PRINT_KERNEL_CACHE_TYPE(a)  (\
  (a)   == CacheTypeCacheless ? "Cacheless" : \
  ((a)  == CacheTypeMkext     ? "Mkext" : \
  (((a) == CacheTypePrelinked ? "Prelinked" : "Kernel"))))

//
// As PageCount is UINT16, we can only index 2^16 * 4096 Bytes with one chain.
//
#define PRELINKED_KEXTS_MAX_SIZE  (BIT16 * MACHO_PAGE_SIZE)

//
// Failsafe default for plist reserve allocation.
//
#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

//
// Make integral kernel version (major, minor, revision).
//
#define KERNEL_VERSION(A, B, C)  ((A) * 10000 + (B) * 100 + (C))

//
// Darwin versions for each release.
//
#define KERNEL_VERSION_TIGER          8
#define KERNEL_VERSION_LEOPARD        9
#define KERNEL_VERSION_SNOW_LEOPARD   10
#define KERNEL_VERSION_LION           11
#define KERNEL_VERSION_MOUNTAIN_LION  12
#define KERNEL_VERSION_MAVERICKS      13
#define KERNEL_VERSION_YOSEMITE       14
#define KERNEL_VERSION_EL_CAPITAN     15
#define KERNEL_VERSION_SIERRA         16
#define KERNEL_VERSION_HIGH_SIERRA    17
#define KERNEL_VERSION_MOJAVE         18
#define KERNEL_VERSION_CATALINA       19
#define KERNEL_VERSION_BIG_SUR        20
#define KERNEL_VERSION_MONTEREY       21
#define KERNEL_VERSION_VENTURA        22

//
// Minimum kernel versions for each release.
//
#define KERNEL_VERSION_TIGER_MIN          KERNEL_VERSION (KERNEL_VERSION_TIGER, 0, 0)
#define KERNEL_VERSION_LEOPARD_MIN        KERNEL_VERSION (KERNEL_VERSION_LEOPARD, 0, 0)
#define KERNEL_VERSION_SNOW_LEOPARD_MIN   KERNEL_VERSION (KERNEL_VERSION_SNOW_LEOPARD, 0, 0)
#define KERNEL_VERSION_LION_MIN           KERNEL_VERSION (KERNEL_VERSION_LION, 0, 0)
#define KERNEL_VERSION_MOUNTAIN_LION_MIN  KERNEL_VERSION (KERNEL_VERSION_MOUNTAIN_LION, 0, 0)
#define KERNEL_VERSION_MAVERICKS_MIN      KERNEL_VERSION (KERNEL_VERSION_MAVERICKS, 0, 0)
#define KERNEL_VERSION_YOSEMITE_MIN       KERNEL_VERSION (KERNEL_VERSION_YOSEMITE, 0, 0)
#define KERNEL_VERSION_EL_CAPITAN_MIN     KERNEL_VERSION (KERNEL_VERSION_EL_CAPITAN, 0, 0)
#define KERNEL_VERSION_SIERRA_MIN         KERNEL_VERSION (KERNEL_VERSION_SIERRA, 0, 0)
#define KERNEL_VERSION_HIGH_SIERRA_MIN    KERNEL_VERSION (KERNEL_VERSION_HIGH_SIERRA, 0, 0)
#define KERNEL_VERSION_MOJAVE_MIN         KERNEL_VERSION (KERNEL_VERSION_MOJAVE, 0, 0)
#define KERNEL_VERSION_CATALINA_MIN       KERNEL_VERSION (KERNEL_VERSION_CATALINA, 0, 0)
#define KERNEL_VERSION_BIG_SUR_MIN        KERNEL_VERSION (KERNEL_VERSION_BIG_SUR, 0, 0)
#define KERNEL_VERSION_MONTEREY_MIN       KERNEL_VERSION (KERNEL_VERSION_MONTEREY, 0, 0)
#define KERNEL_VERSION_VENTURA_MIN        KERNEL_VERSION (KERNEL_VERSION_VENTURA, 0, 0)

//
// Maximum kernel versions for each release.
//
#define KERNEL_VERSION_TIGER_MAX          (KERNEL_VERSION_LEOPARD_MIN - 1)
#define KERNEL_VERSION_LEOPARD_MAX        (KERNEL_VERSION_SNOW_LEOPARD_MIN - 1)
#define KERNEL_VERSION_SNOW_LEOPARD_MAX   (KERNEL_VERSION_LION_MIN - 1)
#define KERNEL_VERSION_LION_MAX           (KERNEL_VERSION_MOUNTAIN_LION_MIN - 1)
#define KERNEL_VERSION_MOUNTAIN_LION_MAX  (KERNEL_VERSION_MAVERICKS_MIN - 1)
#define KERNEL_VERSION_MAVERICKS_MAX      (KERNEL_VERSION_YOSEMITE_MIN - 1)
#define KERNEL_VERSION_YOSEMITE_MAX       (KERNEL_VERSION_EL_CAPITAN_MIN - 1)
#define KERNEL_VERSION_EL_CAPITAN_MAX     (KERNEL_VERSION_SIERRA_MIN - 1)
#define KERNEL_VERSION_SIERRA_MAX         (KERNEL_VERSION_HIGH_SIERRA_MIN - 1)
#define KERNEL_VERSION_HIGH_SIERRA_MAX    (KERNEL_VERSION_MOJAVE_MIN - 1)
#define KERNEL_VERSION_MOJAVE_MAX         (KERNEL_VERSION_CATALINA_MIN - 1)
#define KERNEL_VERSION_CATALINA_MAX       (KERNEL_VERSION_BIG_SUR_MIN - 1)
#define KERNEL_VERSION_BIG_SUR_MAX        (KERNEL_VERSION_MONTEREY_MIN - 1)
#define KERNEL_VERSION_MONTEREY_MAX       (KERNEL_VERSION_VENTURA_MIN - 1)

//
// Prelinked context used for kernel modification.
//
typedef struct {
  //
  // Current version of prelinkedkernel. It takes a reference of user-allocated
  // memory block from pool, and grows if needed.
  //
  UINT8                                  *Prelinked;
  //
  // Exportable prelinkedkernel size, i.e. the payload size. Also references user field.
  //
  UINT32                                 PrelinkedSize;
  //
  // Currently allocated prelinkedkernel size, used for reduced rellocations.
  //
  UINT32                                 PrelinkedAllocSize;
  //
  // Current last virtual address (kext source files and plist are put here).
  //
  UINT64                                 PrelinkedLastAddress;
  //
  // Current last virtual load address (kexts are loaded here after kernel startup).
  //
  UINT64                                 PrelinkedLastLoadAddress;
  //
  // Mach-O context for prelinkedkernel.
  //
  OC_MACHO_CONTEXT                       PrelinkedMachContext;
  //
  // Pointer to PRELINK_INFO_SEGMENT.
  //
  MACH_SEGMENT_COMMAND_ANY               *PrelinkedInfoSegment;
  //
  // Pointer to PRELINK_INFO_SECTION.
  //
  MACH_SECTION_ANY                       *PrelinkedInfoSection;
  //
  // Pointer to PRELINK_TEXT_SEGMENT (for prelinkedkernel, NULL for KC).
  // As of macOS 13 Developer Beta 3, this segment may have corrupted
  // information.
  //
  MACH_SEGMENT_COMMAND_ANY               *PrelinkedTextSegment;
  //
  // Pointer to PRELINK_TEXT_SECTION.
  //
  MACH_SECTION_ANY                       *PrelinkedTextSection;
  //
  // Pointer to PRELINK_STATE_SEGMENT (for 10.6.8).
  //
  MACH_SEGMENT_COMMAND_ANY               *PrelinkedStateSegment;
  //
  // Pointer to PRELINK_STATE_SECTION_KERNEL (for 10.6.8).
  //
  MACH_SECTION_ANY                       *PrelinkedStateSectionKernel;
  //
  // Pointer to PRELINK_STATE_SECTION_KEXTS (for 10.6.8).
  //
  MACH_SECTION_ANY                       *PrelinkedStateSectionKexts;
  //
  // Contents of PRELINK_STATE_SECTION_KERNEL section (for 10.6.8).
  //
  VOID                                   *PrelinkedStateKernel;
  //
  // Contents of PRELINK_STATE_SECTION_KEXTS (for 10.6.8).
  //
  VOID                                   *PrelinkedStateKexts;
  //
  // PRELINK_STATE_SECTION_KEXTS original load address.
  //
  UINT64                                 PrelinkedStateKextsAddress;
  //
  // PRELINK_STATE_SECTION_KERNEL section size (for 10.6.8).
  //
  UINT32                                 PrelinkedStateKernelSize;
  //
  // PRELINK_STATE_SECTION_KEXTS section size (for 10.6.8).
  //
  UINT32                                 PrelinkedStateKextsSize;
  //
  // Pointer to KC_LINKEDIT_SEGMENT (for KC mode).
  //
  MACH_SEGMENT_COMMAND_ANY               *LinkEditSegment;
  //
  // Pointer to KC_REGION0_SEGMENT (for KC mode).
  //
  MACH_SEGMENT_COMMAND_64                *RegionSegment;
  //
  // Mach-O context for inner prelinkedkernel (for KC mode).
  //
  OC_MACHO_CONTEXT                       InnerMachContext;
  //
  // Pointer to PRELINK_INFO_SEGMENT in the inner prelinkedkernel.
  //
  MACH_SEGMENT_COMMAND_64                *InnerInfoSegment;
  //
  // Pointer to PRELINK_INFO_SECTION in the inner prelinkedkernel.
  //
  MACH_SECTION_64                        *InnerInfoSection;
  //
  // Copy of prelinkedkernel PRELINK_INFO_SECTION used for XML_DOCUMENT.
  // Freed upon context destruction.
  //
  CHAR8                                  *PrelinkedInfo;
  //
  // Parsed instance of PlistInfo. New entries are added here.
  //
  XML_DOCUMENT                           *PrelinkedInfoDocument;
  //
  // Reference for PRELINK_INFO_DICTIONARY_KEY in PlistDocument.
  // This reference is used for quick path during kext injection.
  //
  XML_NODE                               *KextList;
  //
  // Plist scratch buffer used when updating values.
  //
  CHAR8                                  *KextScratchBuffer;
  //
  // Buffers allocated from pool for internal needs.
  //
  VOID                                   **PooledBuffers;
  //
  // Currently used pooled buffers.
  //
  UINT32                                 PooledBuffersCount;
  //
  // Currently allocated pooled buffers. PooledBuffersAllocCount >= PooledBuffersCount.
  //
  UINT32                                 PooledBuffersAllocCount;
  VOID                                   *LinkBuffer;
  UINT32                                 LinkBufferSize;
  //
  // Used for caching all prelinked kexts.
  // I.e. this contains kernel, injected kexts, and kexts used as dependencies.
  //
  LIST_ENTRY                             PrelinkedKexts;
  //
  // Used for caching prelinked kexts, which we inject.
  // This is a sublist of PrelinkedKexts.
  //
  LIST_ENTRY                             InjectedKexts;
  //
  // Whether this kernel is a kernel collection (used by macOS 11.0+).
  //
  BOOLEAN                                IsKernelCollection;
  //
  // Kext segment file location for kernel collection.
  //
  UINT32                                 KextsFileOffset;
  //
  // Kext segment memory location for kernel collection.
  //
  UINT64                                 KextsVmAddress;
  //
  // Kext fixup chain for kernel collection.
  //
  MACH_DYLD_CHAINED_STARTS_IN_SEGMENT    *KextsFixupChains;
  //
  // Kernel virtual base.
  //
  UINT64                                 VirtualBase;
  //
  // Prelinked is 32-bit.
  //
  BOOLEAN                                Is32Bit;
} PRELINKED_CONTEXT;

//
// Kernel and kext patching context.
//
typedef struct {
  //
  // Mach-O context for patched binary.
  //
  OC_MACHO_CONTEXT    MachContext;
  //
  // Virtual base of text segment.
  //
  UINT64              VirtualBase;
  //
  // File offset.
  //
  UINT64              FileOffset;
  //
  // Virtual kmod_info_t address.
  //
  UINT64              VirtualKmod;
  //
  // Pointer to KXLD state (read only, it is allocated in PrelinkedStateKexts).
  //
  CONST VOID          *KxldState;
  //
  // Pointer to KXLD state (read only, it is allocated in PrelinkedStateKexts).
  //
  UINT32              KxldStateSize;
  //
  // Binary is 32-bit.
  //
  BOOLEAN             Is32Bit;
  //
  // Patcher context is contained within a kernel collection.
  //
  BOOLEAN             IsKernelCollection;
} PATCHER_CONTEXT;

//
// Kernel and kext patch description.
//
typedef struct {
  //
  // Comment or NULL.
  //
  CONST CHAR8    *Comment;
  //
  // Symbol base or NULL (0 base is used then).
  //
  CONST CHAR8    *Base;
  //
  // Find bytes or NULL (data is written to base then).
  //
  CONST UINT8    *Find;
  //
  // Replace bytes.
  //
  CONST UINT8    *Replace;
  //
  // Find mask or NULL.
  //
  CONST UINT8    *Mask;
  //
  // Replace mask or NULL.
  //
  CONST UINT8    *ReplaceMask;
  //
  // Patch size.
  //
  UINT32         Size;
  //
  // Replace count or 0 for all.
  //
  UINT32         Count;
  //
  // Skip count or 0 to start from 1 match.
  //
  UINT32         Skip;
  //
  // Limit replacement size to this value or 0, which assumes table size.
  //
  UINT32         Limit;
} PATCHER_GENERIC_PATCH;

//
// Context for cacheless boot (S/L/E).
//
typedef struct {
  //
  // Extensions directory EFI_FILE_PROTOCOL instance.
  //
  EFI_FILE_PROTOCOL    *ExtensionsDir;
  //
  // Extensions directory filename. This is freed by the caller.
  //
  CONST CHAR16         *ExtensionsDirFileName;
  //
  // List of injected kexts.
  //
  LIST_ENTRY           InjectedKexts;
  //
  // List of dependencies that need to be injected.
  //
  LIST_ENTRY           InjectedDependencies;
  //
  // List of kext patches for built-in shipping kexts.
  //
  LIST_ENTRY           PatchedKexts;
  //
  // List of built-in shipping kexts.
  //
  LIST_ENTRY           BuiltInKexts;
  //
  // Current kernel version.
  //
  UINT32               KernelVersion;
  //
  // System is booting in 32-bit mode.
  //
  BOOLEAN              Is32Bit;
  //
  // Flag to indicate if above list is valid. List is built during the first read from SLE.
  //
  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             Is32Bit;
  //
  // 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;
  //
  // List of cached kexts, used for patching and blocking.
  //
  LIST_ENTRY          CachedKexts;
} MKEXT_CONTEXT;

//
// Kernel quirk names.
//
typedef enum {
  //
  // Apply MSR E2 patches to AppleIntelCPUPowerManagement kext.
  //
  KernelQuirkAppleCpuPmCfgLock,
  //
  // Apply MSR E2 patches to XNU kernel (XCPM).
  //
  KernelQuirkAppleXcpmCfgLock,
  //
  // Apply extra MSR patches to XNU kernel (XCPM).
  //
  KernelQuirkAppleXcpmExtraMsrs,
  //
  // Apply max MSR_IA32_PERF_CONTROL patches to XNU kernel (XCPM).
  //
  KernelQuirkAppleXcpmForceBoost,
  //
  // Apply regiser base change patch for customised PCI serial device to XNU.
  //
  KernelQuirkCustomPciSerialDevice,
  //
  // Apply custom AppleSMBIOS kext GUID patch for Custom UpdateSMBIOSMode.
  //
  KernelQuirkCustomSmbiosGuid1,
  KernelQuirkCustomSmbiosGuid2,
  //
  // Apply VT-d disabling patches to IOPCIFamily kext to disable IOMapper in macOS.
  //
  KernelQuirkDisableIoMapper,
  //
  // Disable mapping PCI bridge device memory in IOMMU (VT-d) to resolve compatibility issues.
  //
  KernelQuirkDisableIoMapperMapping,
  //
  // Disable AppleRTC checksum writing.
  //
  KernelQuirkDisableRtcChecksum,
  //
  // Apply dummy power management patches to AppleIntelCPUPowerManagement in macOS.
  //
  KernelQuirkDummyPowerManagement,
  //
  // Apply feature flags patches to IOBluetoothFamily kext to ensure full Bluetooth functionality.
  //
  KernelQuirkExtendBTFeatureFlags,
  //
  // Apply icon type patches to IOAHCIPort kext to force internal disk icons.
  //
  KernelQuirkExternalDiskIcons,
  //
  // Enable Aquantia AQtion AQC-107s support.
  //
  KernelQuirkForceAquantiaEthernet,
  //
  // Force SecureBoot support for all CPUs.
  //
  KernelQuirkForceSecureBootScheme,
  //
  // Apply PCI bar size patches to IOPCIFamily kext for compatibility with select configuration.
  //
  KernelQuirkIncreasePciBarSize,
  //
  // Disable LAPIC interrupt kernel panic on AP cores.
  //
  KernelQuirkLapicKernelPanic,
  //
  // Replace the 64-bit commpage bcopy implementation with one that does not use SSSE3.
  //
  KernelQuirkLegacyCommpage,
  //
  // Apply kernel patches to remove kext dumping in the panic log.
  //
  KernelQuirkPanicNoKextDump,
  //
  // Disable power state change timeout kernel panic (10.15+).
  //
  KernelQuirkPowerTimeoutKernelPanic,
  //
  // Remove kernel __LINKEDIT jetisson.
  //
  KernelQuirkSegmentJettison,
  //
  // Set custom APFS trim timeout.
  //
  KernelQuirkSetApfsTrimTimeout,
  //
  // Apply vendor patches to IOAHCIFamily kext to enable native features for third-party drives,
  // such as TRIM on SSDs or hibernation support on 10.15.
  //
  KernelQuirkThirdPartyDrives,
  //
  // Apply port limit patches to AppleUSBXHCI and AppleUSBXHCIPCI kexts.
  //
  KernelQuirkXhciPortLimit1,
  KernelQuirkXhciPortLimit2,
  KernelQuirkXhciPortLimit3,

  KernelQuirkMax
} KERNEL_QUIRK_NAME;

/**
  Kernel quirk patch function.

  @param[in,out]  Patcher        A pointer to the patcher context. Only optional for kext patching.
  @param[in]      KernelVersion  Kernel version to be matched.

  @return  EFI_SUCCESS when the patch is successfully applied.
**/
typedef
EFI_STATUS
(KERNEL_QUIRK_PATCH_FUNCTION) (
  IN OUT PATCHER_CONTEXT  *Patcher OPTIONAL,
  IN     UINT32           KernelVersion
  );

//
// Kernel quirk.
//
typedef struct {
  //
  // Target bundle identifier. NULL for kernel.
  //
  CONST CHAR8                    *Identifier;
  //
  // Quirk patch function.
  //
  KERNEL_QUIRK_PATCH_FUNCTION    *PatchFunction;
} KERNEL_QUIRK;

/**
  Applies the specified quirk.

  @param[in]     Name           KERNEL_QUIRK_NAME specifying the quirk name.
  @param[in,out] Patcher        PATCHER_CONTEXT instance. Only optional for kext patching.
  @param[in]     KernelVersion  Current kernel version.

  @returns EFI_SUCCESS on success.
**/
EFI_STATUS
KernelApplyQuirk (
  IN     KERNEL_QUIRK_NAME  Name,
  IN OUT PATCHER_CONTEXT    *Patcher OPTIONAL,
  IN     UINT32             KernelVersion
  );

/**
  Read Apple kernel 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]      Prefer32Bit    Prefer 32-bit in case of fat binary.
  @param[out]     Is32Bit        Whether resulting kernel is 32-bit or not.
  @param[in,out]  Kernel         Resulting non-fat kernel buffer from pool.
  @param[out]     KernelSize     Actual kernel size.
  @param[out]     AllocatedSize  Allocated kernel size (AllocatedSize >= KernelSize).
  @param[in]      ReservedSize   Allocated extra size for added kernel extensions.
  @param[out]     Digest         SHA-384 digest for the original kernel, optional.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
ReadAppleKernel (
  IN     EFI_FILE_PROTOCOL  *File,
  IN     BOOLEAN            Prefer32Bit,
  OUT BOOLEAN               *Is32Bit,
  OUT UINT8                 **Kernel,
  OUT UINT32                *KernelSize,
  OUT UINT32                *AllocatedSize,
  IN     UINT32             ReservedSize,
  OUT UINT8                 *Digest  OPTIONAL
  );

/**
  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]     Prefer32Bit      Prefer 32-bit in case of fat binary.
  @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     BOOLEAN            Prefer32Bit,
  OUT UINT8                 **Mkext,
  OUT UINT32                *MkextSize,
  OUT UINT32                *AllocatedSize,
  IN     UINT32             ReservedSize,
  IN     UINT32             NumReservedKexts
  );

/**
  Create Apple kernel version in integer format.
  See KERNEL_VERSION on how to build it from integers.

  @param[in]  String      Apple kernel version.

  @returns Kernel version or 0.
**/
UINT32
OcParseDarwinVersion (
  IN  CONST CHAR8  *String
  );

/**
  Read Apple kernel version in integer format.

  @param[in]  Kernel      Apple kernel binary.
  @param[in]  KernelSize  Apple kernel size.

  @returns Kernel version or 0.
**/
UINT32
OcKernelReadDarwinVersion (
  IN  CONST UINT8  *Kernel,
  IN  UINT32       KernelSize
  );

/**
  Check whether min <= current <= max is true.
  When current or max are 0 they are considered infinite.

  @param[in]  CurrentVersion  Current kernel version.
  @param[in]  MinVersion      Minimal kernel version.
  @param[in]  MaxVersion      Maximum kernel version.

  @returns TRUE on match.
**/
BOOLEAN
OcMatchDarwinVersion (
  IN  UINT32  CurrentVersion OPTIONAL,
  IN  UINT32  MinVersion     OPTIONAL,
  IN  UINT32  MaxVersion     OPTIONAL
  );

/**
  Construct prelinked context for later modification.
  Must be freed with PrelinkedContextFree on success.
  Note, that PrelinkedAllocSize never changes, and is to be estimated.

  @param[in,out] Context             Prelinked context.
  @param[in,out] Prelinked           Unpacked prelinked buffer (Mach-O image).
  @param[in]     PrelinkedSize       Unpacked prelinked buffer size.
  @param[in]     PrelinkedAllocSize  Unpacked prelinked buffer allocated size.
  @param[in]     Is32Bit             TRUE if prelinked is 32-bit.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedContextInit (
  IN OUT  PRELINKED_CONTEXT  *Context,
  IN OUT  UINT8              *Prelinked,
  IN      UINT32             PrelinkedSize,
  IN      UINT32             PrelinkedAllocSize,
  IN      BOOLEAN            Is32Bit
  );

/**
  Free resources consumed by prelinked context.

  @param[in,out] Context  Prelinked context.
**/
VOID
PrelinkedContextFree (
  IN OUT  PRELINKED_CONTEXT  *Context
  );

/**
  Insert pool-allocated buffer dependency with the same lifetime as
  prelinked context, so it gets freed with PrelinkedContextFree.

  @param[in,out] Context          Prelinked context.
  @param[in]     Buffer           Pool allocated buffer.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedDependencyInsert (
  IN OUT  PRELINKED_CONTEXT  *Context,
  IN      VOID               *Buffer
  );

/**
  Drop current plist entry, required for kext injection.
  Ensure that prelinked text can grow with new kexts.

  @param[in,out] Context          Prelinked context.
  @param[in]     LinkedExpansion  Extra LINKEDIT size for KC required
                                  to hold DYLD chained fixups.

  @retval  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedInjectPrepare (
  IN OUT PRELINKED_CONTEXT  *Context,
  IN     UINT32             LinkedExpansion,
  IN     UINT32             ReservedExeSize
  );

/**
  Insert current plist entry after kext injection.

  @param[in,out] Context  Prelinked context.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedInjectComplete (
  IN OUT PRELINKED_CONTEXT  *Context
  );

/**
  Updated required 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.
  @param[in]     Is32Bit           TRUE to process 32-bit kext.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedReserveKextSize (
  IN OUT UINT32   *ReservedInfoSize,
  IN OUT UINT32   *ReservedExeSize,
  IN     UINT32   InfoPlistSize,
  IN     UINT8    *Executable OPTIONAL,
  IN     UINT32   ExecutableSize OPTIONAL,
  IN     BOOLEAN  Is32Bit
  );

/**
  Perform kext injection.

  @param[in,out] Context         Prelinked context.
  @param[in]     Identifier      Kext bundle identifier. If a kext with this identifier
                                 already exists, the kext will not be injected. Optional.
  @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] ExecutablePath  Kext executable path (e.g. Contents/MacOS/mykext), optional.
  @param[in,out] Executable      Kext executable, optional.
  @param[in]     ExecutableSize  Kext executable size, optional.
  @param[out]    BundleVersion   Kext bundle version, optionally set on request.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedInjectKext (
  IN OUT PRELINKED_CONTEXT  *Context,
  IN     CONST CHAR8        *Identifier OPTIONAL,
  IN     CONST CHAR8        *BundlePath,
  IN     CONST CHAR8        *InfoPlist,
  IN     UINT32             InfoPlistSize,
  IN     CONST CHAR8        *ExecutablePath OPTIONAL,
  IN OUT CONST UINT8        *Executable OPTIONAL,
  IN     UINT32             ExecutableSize OPTIONAL,
  OUT    CHAR8              BundleVersion[MAX_INFO_BUNDLE_VERSION_KEY_SIZE] OPTIONAL
  );

/**
  Apply kext patch to prelinked.

  @param[in,out] Context         Prelinked context.
  @param[in]     Identifier      Kext bundle identifier.
  @param[in]     Patch           Patch to apply.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedContextApplyPatch (
  IN OUT PRELINKED_CONTEXT      *Context,
  IN     CONST CHAR8            *Identifier,
  IN     PATCHER_GENERIC_PATCH  *Patch
  );

/**
  Apply kext quirk to prelinked.

  @param[in,out] Context         Prelinked context.
  @param[in]     Quirk           Kext quirk to apply.
  @param[in]     KernelVersion   Current kernel version.

  @retval EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedContextApplyQuirk (
  IN OUT PRELINKED_CONTEXT  *Context,
  IN     KERNEL_QUIRK_NAME  Quirk,
  IN     UINT32             KernelVersion
  );

/**
  Block kext in prelinked.

  @param[in,out] Context         Prelinked context.
  @param[in]     Identifier      Kext bundle identifier.
  @param[in]     Exclude         TRUE to exclude kext from prelinked.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PrelinkedContextBlock (
  IN OUT PRELINKED_CONTEXT  *Context,
  IN     CONST CHAR8        *Identifier,
  IN     BOOLEAN            Exclude
  );

/**
  Update Mach-O header with new commands.

  @Param[in,out] Context      Prelinked context.

  @retval EFI_SUCCESS on success.
**/
EFI_STATUS
KcRebuildMachHeader (
  IN OUT PRELINKED_CONTEXT  *Context
  );

/**
  Returns the size required to store a segment's fixup chains information.

  @param[in] SegmentSize  The size, in bytes, of the segment to index.

  @retval 0      The segment is too large to index with a single structure.
  @retval other  The size, in bytes, required to store a segment's fixup chain
                 information.
**/
UINT32
KcGetSegmentFixupChainsSize (
  IN UINT32  SegmentSize
  );

/**
  Initialises a structure that stores a segments's fixup chains information.

  @param[out] SegChain      The information structure to initialise.
  @param[in]  SegChainSize  The size, in bytes, available to SegChain.
  @param[in]  VmAddress     The virtual address of the segment to index.
  @param[in]  VmSize        The virtual size of the segment to index.
**/
EFI_STATUS
KcInitKextFixupChains (
  IN OUT PRELINKED_CONTEXT  *Context,
  IN     UINT32             SegChainSize,
  IN     UINT32             ReservedSize
  );

/**
  Indexes all relocations of MachContext into the kernel described by Context.

  @param[in,out] Context      Prelinked context.
  @param[in]     MachContext  The context of the Mach-O to index. It must have
                              been prelinked by OcAppleKernelLib. The image
                              must reside in Segment.
**/
VOID
KcKextIndexFixups (
  IN OUT PRELINKED_CONTEXT  *Context,
  IN     OC_MACHO_CONTEXT   *MachContext
  );

/**
  Retrieves a KC KEXT's virtual size.

  @param[in] Context        Prelinked context.
  @param[in] SourceAddress  The virtual address within the KC image of the KEXT.

  @retval 0      An error has occured.
  @retval other  The virtual size, in bytes, of the KEXT at SourceAddress.
**/
UINT32
KcGetKextSize (
  IN PRELINKED_CONTEXT  *Context,
  IN UINT64             SourceAddress
  );

/**
  Apply the delta from KC header to the file's offsets.

  @param[in]     PrelinkedContext  Prelinked context.
  @param[in,out] Context           The context of the KEXT to rebase.
  @param[in]     Delta             The offset from KC header the KEXT starts at.

  @retval EFI_SUCCESS  The file has beem rebased successfully.
  @retval other        An error has occured.
**/
EFI_STATUS
KcKextApplyFileDelta (
  IN     PRELINKED_CONTEXT  *PrelinkedContext,
  IN OUT OC_MACHO_CONTEXT   *Context,
  IN     UINT32             Delta
  );

/**
  Update address or dyld fixup value to real address.

  @param[in] Value           Value or fixup.
  @param[in] Name            Source name, optional.

  @return real address on sucess.
**/
UINT64
KcFixupValue (
  IN UINT64       Value,
  IN CONST CHAR8  *Name OPTIONAL
  );

/**
  Initialize patcher from prelinked context for kext patching.

  @param[in,out] Context         Patcher context.
  @param[in,out] Prelinked       Prelinked context.
  @param[in]     Name            Kext bundle identifier.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherInitContextFromPrelinked (
  IN OUT PATCHER_CONTEXT    *Context,
  IN OUT PRELINKED_CONTEXT  *Prelinked,
  IN     CONST CHAR8        *Name
  );

/**
  Initialize patcher from mkext context for kext patching.

  @param[in,out] Context         Patcher context.
  @param[in,out] Mkext           Mkext context.
  @param[in]     Name            Kext bundle identifier.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherInitContextFromMkext (
  IN OUT PATCHER_CONTEXT  *Context,
  IN OUT MKEXT_CONTEXT    *Mkext,
  IN     CONST CHAR8      *Name
  );

/**
  Initialize patcher from buffer for e.g. kernel patching.

  @param[in,out] Context         Patcher context.
  @param[in,out] Buffer          Kernel buffer (could be prelinked).
  @param[in]     BufferSize      Kernel buffer size.
  @param[in]     Is32Bit         TRUE to use 32-bit.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherInitContextFromBuffer (
  IN OUT PATCHER_CONTEXT  *Context,
  IN OUT UINT8            *Buffer,
  IN     UINT32           BufferSize,
  IN     BOOLEAN          Use32Bit
  );

/**
  Get local symbol address.

  @param[in,out] Context         Patcher context.
  @param[in]     Name            Symbol name.
  @param[in,out] Address         Returned symbol address in file.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherGetSymbolAddress (
  IN OUT PATCHER_CONTEXT  *Context,
  IN     CONST CHAR8      *Name,
  IN OUT UINT8            **Address
  );

/**
  Get local symbol value.

  @param[in,out] Context         Patcher context.
  @param[in]     Name            Symbol name.
  @param[in,out] Value           Returned original symbol value.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherGetSymbolValue (
  IN OUT PATCHER_CONTEXT  *Context,
  IN     CONST CHAR8      *Name,
  IN OUT UINT64           *Value
  );

/**
  Get local symbol address and symbol value.

  @param[in,out] Context         Patcher context.
  @param[in]     Name            Symbol name.
  @param[in,out] Address         Returned symbol address in file.
  @param[in,out] Value           Returned original symbol value.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherGetSymbolAddressValue (
  IN OUT PATCHER_CONTEXT  *Context,
  IN     CONST CHAR8      *Name,
  IN OUT UINT8            **Address,
  IN OUT UINT64           *Value
  );

/**
  Apply generic patch.

  @param[in,out] Context         Patcher context.
  @param[in]     Patch           Patch description.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherApplyGenericPatch (
  IN OUT PATCHER_CONTEXT        *Context,
  IN     PATCHER_GENERIC_PATCH  *Patch
  );

/**
  Exclude kext from prelinked.

  @param[in]      Identifier        Kext identifier to be excluded.
  @param[in,out]  PatcherContext    Patcher context.
  @param[in,out]  PrelinkedContext  Prelinked context.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherExcludePrelinkedKext (
  IN     CONST CHAR8        *Identifier,
  IN OUT PATCHER_CONTEXT    *PatcherContext,
  IN OUT PRELINKED_CONTEXT  *PrelinkedContext
  );

/**
  Exclude kext from mkext.

  @param[in,out]  MkextContext  Mkext context.
  @param[in]      Identifier    Kext identifier to be excluded.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherExcludeMkextKext (
  IN OUT MKEXT_CONTEXT  *MkextContext,
  IN     CONST CHAR8    *Identifier
  );

/**
  Block kext from loading.

  @param[in,out] Context         Patcher context.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatcherBlockKext (
  IN OUT PATCHER_CONTEXT  *Context
  );

/**
  Find kmod address.

  @param[in]  ExecutableContext   Mach-O context.
  @param[in]  LoadAddress         Load address.
  @param[in]  Size                Executable size.
  @param[out] Kmod                Pointer to kmod.

  @return  TRUE on success.
**/
BOOLEAN
KextFindKmodAddress (
  IN  OC_MACHO_CONTEXT  *ExecutableContext,
  IN  UINT64            LoadAddress,
  IN  UINT32            Size,
  OUT UINT64            *Kmod
  );

/**
  Set timeout value in microseconds for KernelQuirkSetApfsTrimTimeout quirk.

  @param[in]  Timeout   Timeout in microseconds.
**/
VOID
PatchSetApfsTimeout (
  IN UINT32  Timeout
  );

/**
  Set PCI serial device properties for KernelQuirkCustomPciSerialDevice quirk.

  @param[in]  RegisterBase   PCI serial device regiser base.
  @param[in]  RegisterStride PCI serial device regiser stride.
**/
VOID
PatchSetPciSerialDevice (
  IN  UINTN   RegisterBase,
  IN  UINT32  RegisterStride
  );

/**
  Apply modification to CPUID 1.

  @param Patcher        Patcher context.
  @param CpuInfo        CPU information.
  @param Data           4 32-bit integers with CPUID data.
  @param DataMask       4 32-bit integers with CPUID enabled overrides data.
  @param KernelVersion  Current kernel version.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatchKernelCpuId (
  IN OUT PATCHER_CONTEXT  *Patcher,
  IN     OC_CPU_INFO      *CpuInfo,
  IN     UINT32           *Data,
  IN     UINT32           *DataMask,
  IN     UINT32           KernelVersion
  );

/**
  Apply current CPU information patches.

  @param Patcher        Patcher context.
  @param CpuInfo        CPU information.
  @param KernelVersion  Current kernel version.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
PatchProvideCurrentCpuInfo (
  IN OUT PATCHER_CONTEXT  *Patcher,
  IN     OC_CPU_INFO      *CpuInfo,
  IN     UINT32           KernelVersion
  );

/**
  Initializes cacheless context for later modification.
  Must be freed with CachelessContextFree on success.

  @param[in,out] Context             Cacheless context.
  @param[in]     FileName            Extensions directory filename.
  @param[in]     ExtensionsDir       Extensions directory EFI_FILE_PROTOCOL.
  @param[in]     KernelVersion       Current kernel version.
  @param[in]     Is32Bit             TRUE if booting in 32-bit kernel mode.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextInit (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     CONST CHAR16       *FileName,
  IN     EFI_FILE_PROTOCOL  *ExtensionsDir,
  IN     UINT32             KernelVersion,
  IN     BOOLEAN            Is32Bit
  );

/**
  Frees cacheless context.

  @param[in,out] Context             Cacheless context.

  @return  EFI_SUCCESS on success.
**/
VOID
CachelessContextFree (
  IN OUT CACHELESS_CONTEXT  *Context
  );

/**
  Add kext to cacheless context to be injected later on.

  @param[in,out] Context         Cacheless context.
  @param[in]     InfoPlist       Kext Info.plist.
  @param[in]     InfoPlistSize   Kext Info.plist size.
  @param[in]     Executable      Kext executable, optional.
  @param[in]     ExecutableSize  Kext executable size, optional.
  @param[out]    BundleVersion   Kext bundle version, optionally set on request.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextAddKext (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     CONST CHAR8        *InfoPlist,
  IN     UINT32             InfoPlistSize,
  IN     UINT8              *Executable OPTIONAL,
  IN     UINT32             ExecutableSize OPTIONAL,
  OUT    CHAR8              BundleVersion[MAX_INFO_BUNDLE_VERSION_KEY_SIZE] OPTIONAL
  );

/**
  Force built-in kext to load.

  @param[in,out] Context         Cacheless context.
  @param[in]     Identifier      Kext bundle identifier.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextForceKext (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     CONST CHAR8        *Identifier
  );

/**
  Add patch to cacheless context to be applied later on.

  @param[in,out] Context         Cacheless context.
  @param[in]     Identifier      Kext bundle identifier.
  @param[in]     Patch           Patch to apply.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextAddPatch (
  IN OUT CACHELESS_CONTEXT      *Context,
  IN     CONST CHAR8            *Identifier,
  IN     PATCHER_GENERIC_PATCH  *Patch
  );

/**
  Add kernel quirk to cacheless context to be applied later on.

  @param[in,out] Context         Cacheless context.
  @param[in]     Quirk           Quirk to apply.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextAddQuirk (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     KERNEL_QUIRK_NAME  Quirk
  );

/**
  Add block request to cacheless context to be applied later on.

  @param[in,out] Context         Cacheless context.
  @param[in]     Identifier      Kext bundle identifier.
  @param[in]     Exclude         TRUE to exclude kext from cacheless context.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextBlock (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     CONST CHAR8        *Identifier,
  IN     BOOLEAN            Exclude
  );

/**
  Creates virtual directory overlay EFI_FILE_PROTOCOL from cacheless context.

  @param[in,out] Context             Cacheless context.
  @param[out]    File                The virtual directory instance.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextOverlayExtensionsDir (
  IN OUT CACHELESS_CONTEXT  *Context,
  OUT EFI_FILE_PROTOCOL     **File
  );

/**
  Perform kext injection.

  @param[in,out] Context         Prelinked context.
  @param[in]     FileName        Filename of kext file to be injected.
  @param[out]    VirtualFile     Newly created virtualised EFI_FILE_PROTOCOL instance.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextPerformInject (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     CONST CHAR16       *FileName,
  OUT EFI_FILE_PROTOCOL     **VirtualFile
  );

/**
  Apply patches to built-in kexts.

  @param[in,out] Context         Prelinked context.
  @param[in]     FileName        Filename of kext file to be injected.
  @param[in]     File            EFI_FILE_PROTOCOL instance of kext file.
  @param[out]    VirtualFile     Newly created virtualised EFI_FILE_PROTOCOL instance.

  @return  EFI_SUCCESS on success. If no patches are applicable, VirtualFile will be NULL.
**/
EFI_STATUS
CachelessContextHookBuiltin (
  IN OUT CACHELESS_CONTEXT  *Context,
  IN     CONST CHAR16       *FileName,
  IN     EFI_FILE_PROTOCOL  *File,
  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.
  @param[in]     Is32Bit           TRUE to process 32-bit kext.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
MkextReserveKextSize (
  IN OUT UINT32   *ReservedInfoSize,
  IN OUT UINT32   *ReservedExeSize,
  IN     UINT32   InfoPlistSize,
  IN     UINT8    *Executable OPTIONAL,
  IN     UINT32   ExecutableSize OPTIONAL,
  IN     BOOLEAN  Is32Bit
  );

/**
  Perform mkext kext injection.

  @param[in,out] Context          Mkext context.
  @param[in]     Identifier       Kext bundle identifier. If a kext with this identifier
                                  already exists, the kext will not be injected. Optional.
  @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.
  @param[out]    BundleVersion   Kext bundle version, optionally set on request.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
MkextInjectKext (
  IN OUT MKEXT_CONTEXT  *Context,
  IN     CONST CHAR8    *Identifier OPTIONAL,
  IN     CONST CHAR8    *BundlePath,
  IN     CONST CHAR8    *InfoPlist,
  IN     UINT32         InfoPlistSize,
  IN     UINT8          *Executable OPTIONAL,
  IN     UINT32         ExecutableSize OPTIONAL,
  OUT    CHAR8          BundleVersion[MAX_INFO_BUNDLE_VERSION_KEY_SIZE] OPTIONAL
  );

/**
  Apply kext patch to mkext.

  @param[in,out] Context         Mkext context.
  @param[in]     Identifier      Kext bundle identifier.
  @param[in]     Patch           Patch to apply.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
MkextContextApplyPatch (
  IN OUT MKEXT_CONTEXT          *Context,
  IN     CONST CHAR8            *Identifier,
  IN     PATCHER_GENERIC_PATCH  *Patch
  );

/**
  Apply kext quirk to mkext.

  @param[in,out] Context         Mkext context.
  @param[in]     Quirk           Kext quirk to apply.
  @param[in]     KernelVersion   Current kernel version.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
MkextContextApplyQuirk (
  IN OUT MKEXT_CONTEXT      *Context,
  IN     KERNEL_QUIRK_NAME  Quirk,
  IN     UINT32             KernelVersion
  );

/**
  Block kext in mkext.

  @param[in,out] Context         Mkext context.
  @param[in]     Identifier      Kext bundle identifier.
  @param[in]     Exclude         TRUE to exclude kext from mkext.

  @return  EFI_SUCCESS on success.
**/
EFI_STATUS
MkextContextBlock (
  IN OUT MKEXT_CONTEXT  *Context,
  IN     CONST CHAR8    *Identifier,
  IN     BOOLEAN        Exclude
  );

/**
  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
  );

#endif // OC_APPLE_KERNEL_LIB_H
