/*
 * Copyright (c) 2008-2021 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#define IOKIT_ENABLE_SHARED_PTR

extern "C" {
#include <string.h>
#include <kern/clock.h>
#include <kern/host.h>
#include <kern/kext_alloc.h>
#include <firehose/tracepoint_private.h>
#include <firehose/chunk_private.h>
#include <os/firehose_buffer_private.h>
#include <vm/vm_map_xnu.h>
#include <kextd/kextd_mach.h>
#include <libkern/kernel_mach_header.h>
#include <libkern/kext_panic_report.h>
#include <libkern/kext_request_keys.h>
#include <libkern/mkext.h>
#include <libkern/prelink.h>
#include <libkern/version.h>
#include <libkern/zlib.h>
#include <mach/host_special_ports.h>
#include <mach/mach_vm.h>
#include <mach/mach_time.h>
#include <uuid/uuid.h>
#include <sys/random.h>
#include <sys/reboot.h>
#include <pexpert/pexpert.h>
#include <pexpert/device_tree.h>

#include <sys/pgo.h>

#if CONFIG_CSR
#include <sys/csr.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#endif /* CONFIG_CSR */
};

#if CONFIG_MACF
#include <sys/kauth.h>
#include <security/mac_framework.h>
#endif

#include <vm/vm_kern_xnu.h>
#include <sys/sysctl.h>
#include <kern/task.h>
#include <os/cpp_util.h>

#include <libkern/OSKextLibPrivate.h>
#include <libkern/c++/OSKext.h>
#include <libkern/c++/OSLib.h>

#include <IOKit/IOLib.h>
#include <IOKit/IOCatalogue.h>
#include <IOKit/IORegistryEntry.h>
#include <IOKit/IOService.h>
#include <IOKit/IOUserServer.h>

#include <IOKit/IOStatisticsPrivate.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOPlatformExpert.h>

#include <san/kasan.h>

#if CONFIG_SPTM
#include <arm64/sptm/sptm.h>
#endif

#if PRAGMA_MARK
#pragma mark External & Internal Function Protos
#endif
/*********************************************************************
*********************************************************************/
extern "C" {
extern int  IODTGetLoaderInfo(const char * key, void ** infoAddr, int * infoSize);
extern void IODTFreeLoaderInfo(const char * key, void * infoAddr, int infoSize);

extern ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va); /* osfmk/machine/pmap.h */
extern int dtrace_keep_kernel_symbols(void);

#if defined(__x86_64__) || defined(__i386__)
extern kern_return_t i386_slide_individual_kext(kernel_mach_header_t *mh, uintptr_t slide);
extern kern_return_t i386_slide_kext_collection_mh_addrs(kernel_mach_header_t *mh, uintptr_t slide, bool adjust_mach_headers);
extern void *ubc_getobject_from_filename(const char *filename, struct vnode **, off_t *file_size);
static void *allocate_kcfileset_map_entry_list(void);
static void add_kcfileset_map_entry(void *map_entry_list, vm_map_offset_t start, vm_map_offset_t size);
static void deallocate_kcfileset_map_entry_list_and_unmap_entries(void *map_entry_list, boolean_t unmap_entries, bool pageable);
int vnode_put(struct vnode *vp);
kern_return_t vm_map_kcfileset_segment(vm_map_offset_t *start, vm_map_offset_t size,
    void *control, vm_object_offset_t fileoffset, vm_prot_t max_prot);
kern_return_t vm_unmap_kcfileset_segment(vm_map_offset_t *start, vm_map_offset_t size);
void * ubc_getobject(struct vnode *vp, __unused int flags);
#endif //(__x86_64__) || defined(__i386__)
}

extern unsigned long gVirtBase;
extern unsigned long gPhysBase;

bool pageableKCloaded = false;
bool auxKCloaded = false;
bool resetAuxKCSegmentOnUnload = false;

extern boolean_t pageablekc_uuid_valid;
extern uuid_t pageablekc_uuid;
extern uuid_string_t pageablekc_uuid_string;

extern boolean_t auxkc_uuid_valid;
extern uuid_t auxkc_uuid;
extern uuid_string_t auxkc_uuid_string;

static OSReturn _OSKextCreateRequest(
	const char    * predicate,
	OSSharedPtr<OSDictionary> & requestP);
static OSString * _OSKextGetRequestPredicate(OSDictionary * requestDict);
static OSObject * _OSKextGetRequestArgument(
	OSDictionary * requestDict,
	const char   * argName);
static bool _OSKextSetRequestArgument(
	OSDictionary    * requestDict,
	const char      * argName,
	OSMetaClassBase * value);
template <typename T>
static T * _OSKextExtractPointer(OSValueObject<T *> * wrapper);
static OSKextRequestResourceCallback _OSKextExtractCallbackPointer(OSValueObject<OSKextRequestResourceCallback> * wrapper);
static OSReturn _OSDictionarySetCStringValue(
	OSDictionary * dict,
	const char   * key,
	const char   * value);
static bool _OSKextInUnloadedPrelinkedKexts(const OSSymbol * theBundleID);
#if CONFIG_KXLD
static bool _OSKextInPrelinkRebuildWindow(void);
#endif

// We really should add containsObject() & containsCString to OSCollection & subclasses.
// So few pad slots, though....
static bool _OSArrayContainsCString(OSArray * array, const char * cString);
static void OSKextLogKextInfo(OSKext *aKext, uint64_t address, uint64_t size, firehose_tracepoint_code_t code);

static const char *getDextUniqueIDCString(OSData *dextUniqueID, unsigned int *size);

/* Prelinked arm kexts do not have VM entries because the method we use to
 * fake an entry (see libsa/bootstrap.cpp:readPrelinkedExtensions()) does
 * not work on ARM.  To get around that, we must free prelinked kext
 * executables with ml_static_mfree() instead of kext_free().
 */
#if __i386__ || __x86_64__
#define VM_MAPPED_KEXTS 1
#define KASLR_KEXT_DEBUG 0
#define KASLR_IOREG_DEBUG 0
#elif __arm__ || __arm64__
#define VM_MAPPED_KEXTS 0
#define KASLR_KEXT_DEBUG 0
#else
#error Unsupported architecture
#endif

#if PRAGMA_MARK
#pragma mark Constants & Macros
#endif
/*********************************************************************
* Constants & Macros
*********************************************************************/

/* Use this number to create containers.
 */
#define kOSKextTypicalLoadCount      (150)
#define kOSKextTypicalUpgradeCount     (5)

/* Any kext will have at least 1 retain for the internal lookup-by-ID dict.
 * A loaded kext will no dependents or external retains will have 2 retains.
 */
#define kOSKextMinRetainCount        (1)
#define kOSKextMinLoadedRetainCount  (2)

#define kOSKextMaxDextLaunchedCount  (~((uint32_t)0))
#define KOSBundleDextUniqueIdentifierMaxStringLength (KOSBundleDextUniqueIdentifierMaxLength * 2 +1)

/**********
 * Strings and substrings used in dependency resolution.
 */
#define APPLE_KEXT_PREFIX            "com.apple."
#define KERNEL_LIB                   "com.apple.kernel"

#define PRIVATE_KPI                  "com.apple.kpi.private"

/* Version for compatbility pseudokexts (com.apple.kernel.*),
 * compatible back to v6.0.
 */
#define KERNEL6_LIB                  "com.apple.kernel.6.0"
#define KERNEL6_VERSION              "7.9.9"

#define KERNEL_LIB_PREFIX            "com.apple.kernel."
#define KPI_LIB_PREFIX               "com.apple.kpi."

#define STRING_HAS_PREFIX(s, p)      (strncmp((s), (p), strlen(p)) == 0)

#define REBUILD_MAX_TIME (60 * 5) // 5 minutes
#define MINIMUM_WAKEUP_SECONDS (30)

/*********************************************************************
* infoDict keys for internally-stored data. Saves on ivar slots for
* objects we don't keep around past boot time or during active load.
*********************************************************************/

/* A usable, uncompressed file is stored under this key.
 */
#define _kOSKextExecutableKey                "_OSKextExecutable"

/* An indirect reference to the executable file from an mkext
 * is stored under this key.
 */
#define _kOSKextMkextExecutableReferenceKey  "_OSKextMkextExecutableReference"

/* If the file is contained in a larger buffer laid down by the booter or
 * sent from user space, the OSKext stores that OSData under this key so that
 * references are properly tracked. This is always an mkext, right now.
 */
#define _kOSKextExecutableExternalDataKey    "_OSKextExecutableExternalData"

#define OS_LOG_HDR_VERSION  1
#define NUM_OS_LOG_SECTIONS 3

#define OS_LOG_SECT_IDX         0
#define CSTRING_SECT_IDX        1
#define ASAN_CSTRING_SECT_IDX   2

#if PRAGMA_MARK
#pragma mark Typedefs
#endif
/*********************************************************************
* Typedefs
*********************************************************************/

/*********************************************************************
* osLogDataHeaderRef describes the header information of an OSData
* object that is returned when querying for kOSBundleLogStringsKey.
* We currently return information regarding 2 sections - os_log and
* cstring. In the case that the os_log section doesn't exist, we just
* return an offset and length of 0 for that section.
*********************************************************************/
typedef struct osLogDataHeader {
	uint32_t version;
	uint32_t sect_count;
	struct {
		uint32_t sect_offset;
		uint32_t sect_size;
	} sections[0];
} osLogDataHeaderRef;

/*********************************************************************
* MkextEntryRef describes the contents of an OSData object
* referencing a file entry from an mkext so that we can uncompress
* (if necessary) and extract it on demand.
*
* It contains the mkextVersion in case we ever wind up supporting
* multiple mkext formats. Mkext format 1 is officially retired as of
* Snow Leopard.
*********************************************************************/
typedef struct MkextEntryRef {
	mkext_basic_header * mkext; // beginning of whole mkext file
	void               * fileinfo;// mkext2_file_entry or equiv; see mkext.h
} MkextEntryRef;

#if PRAGMA_MARK
#pragma mark Global and static Module Variables
#endif
/*********************************************************************
* Global & static variables, used to keep track of kexts.
*********************************************************************/

static  bool                sPrelinkBoot               = false;
static  bool                sSafeBoot                  = false;
static  bool                sKeepSymbols               = false;
static  bool                sPanicOnKCMismatch         = false;
static  bool                sOSKextWasResetAfterUserspaceReboot = false;

/*********************************************************************
 * sKextLock is the principal lock for OSKext, and guards all static
 * and global variables not owned by other locks (declared further
 * below). It must be taken by any entry-point method or function,
 * including internal functions called on scheduled threads.
 *
 * sKextLock and sKextInnerLock are recursive due to multiple functions
 * that are called both externally and internally. The other locks are
 * nonrecursive.
 *
 * Which locks are taken depends on what they protect, but if more than
 * one must be taken, they must always be locked in this order
 * (and unlocked in reverse order) to prevent deadlocks:
 *
 *    1. sKextLock
 *    2. sKextInnerLock
 *    3. sKextSummariesLock
 *    4. sKextLoggingLock
 */
static IORecursiveLock    * sKextLock                  = NULL;

static OSSharedPtr<OSDictionary>   sKextsByID;
static OSSharedPtr<OSDictionary>   sExcludeListByID;
static OSKextVersion               sExcludeListVersion        = 0;
static OSSharedPtr<OSArray>        sLoadedKexts;
static OSSharedPtr<OSDictionary>   sNonLoadableKextsByID;
static OSSharedPtr<OSArray>        sUnloadedPrelinkedKexts;
static OSSharedPtr<OSArray>        sLoadedDriverKitKexts;
static OSSharedPtr<OSDictionary>   sDriverKitToUpgradeByID;

// Requests to the IOKit daemon waiting to be picked up.
static OSSharedPtr<OSArray>        sKernelRequests;
// Identifier of kext load requests in sKernelRequests
static OSSharedPtr<OSSet>          sPostedKextLoadIdentifiers;
static OSSharedPtr<OSArray>        sRequestCallbackRecords;

// Identifiers of all kexts ever requested in kernel; used for prelinked kernel
static OSSharedPtr<OSSet>          sAllKextLoadIdentifiers;
#if CONFIG_KXLD
static KXLDContext        * sKxldContext               = NULL;
#endif
static uint32_t             sNextLoadTag               = 0;
static uint32_t             sNextRequestTag            = 0;

static bool                 sUserLoadsActive           = false;
static bool                 sIOKitDaemonActive         = false;
static bool                 sDeferredLoadSucceeded     = false;
static bool                 sConsiderUnloadsExecuted   = false;

#if NO_KEXTD
static bool                 sKernelRequestsEnabled     = false;
#else
static bool                 sKernelRequestsEnabled     = true;
#endif
static bool                 sLoadEnabled               = true;
static bool                 sUnloadEnabled             = true;

/*********************************************************************
 * Stuff for the OSKext representing the kernel itself.
 **********/
static OSKext          * sKernelKext             = NULL;

/* Load Tag IDs used by statically loaded binaries (e.g, the kernel itself). */
enum : uint32_t {
	kOSKextKernelLoadTag = 0,
#if CONFIG_SPTM
	kOSKextSPTMLoadTag   = 1,
	kOSKextTXMLoadTag    = 2,
#endif /* CONFIG_SPTM */
	kOSKextLoadTagCount
};

/* Set up a fake kmod_info struct for the kernel.
 * It's used in OSRuntime.cpp to call OSRuntimeInitializeCPP()
 * before OSKext is initialized; that call only needs the name
 * and address to be set correctly.
 *
 * We don't do much else with the kerne's kmod_info; we never
 * put it into the kmod list, never adjust the reference count,
 * and never have kernel components reference it.
 * For that matter, we don't do much with kmod_info structs
 * at all anymore! We just keep them filled in for gdb and
 * binary compability.
 */
kmod_info_t g_kernel_kmod_info = {
	.next =            NULL,
	.info_version =    KMOD_INFO_VERSION,
	.id =              kOSKextKernelLoadTag,   // loadTag: kernel is always 0
	.name =            kOSKextKernelIdentifier,// bundle identifier
	.version =         "0",           // filled in in OSKext::initialize()
	.reference_count = -1,            // never adjusted; kernel never unloads
	.reference_list =  NULL,
	.address =         0,
	.size =            0,             // filled in in OSKext::initialize()
	.hdr_size =        0,
	.start =           NULL,
	.stop =            NULL
};

#if CONFIG_SPTM
/* The SPTM and TXM need fake kmod structures just like the kernel. */
kmod_info_t g_sptm_kmod_info = {
	.next =            NULL,
	.info_version =    KMOD_INFO_VERSION,
	.id =              kOSKextSPTMLoadTag,   // Always one after the kernel
	.name =            kOSKextSPTMIdentifier,// bundle identifier
	.version =         "0",           // filled in by OSKext::initialize()
	.reference_count = -1,            // never adjusted; SPTM never unloads
	.reference_list =  NULL,
	.address =         0,
	.size =            0,             // filled in by OSKext::initialize()
	.hdr_size =        0,
	.start =           NULL,
	.stop =            NULL
};

kmod_info_t g_txm_kmod_info = {
	.next =            NULL,
	.info_version =    KMOD_INFO_VERSION,
	.id =              kOSKextTXMLoadTag,   // Always one after the SPTM
	.name =            kOSKextTXMIdentifier,// bundle identifier
	.version =         "0",           // filled in by OSKext::initialize()
	.reference_count = -1,            // never adjusted; TXM never unloads
	.reference_list =  NULL,
	.address =         0,
	.size =            0,             // filled in by OSKext::initialize()
	.hdr_size =        0,
	.start =           NULL,
	.stop =            NULL
};
#endif /* CONFIG_SPTM */

/* Set up a fake kmod_info struct for statically linked kexts that don't have one. */

kmod_info_t invalid_kmod_info = {
	.next =            NULL,
	.info_version =    KMOD_INFO_VERSION,
	.id =              UINT32_MAX,
	.name =            "invalid",
	.version =         "0",
	.reference_count = -1,
	.reference_list =  NULL,
	.address =         0,
	.size =            0,
	.hdr_size =        0,
	.start =           NULL,
	.stop =            NULL
};

extern "C" {
// symbol 'kmod' referenced in: model_dep.c, db_trace.c, symbols.c, db_low_trace.c,
// dtrace.c, dtrace_glue.h, OSKext.cpp, locore.s, lowmem_vectors.s,
// misc_protos.h, db_low_trace.c, kgmacros
// 'kmod' is a holdover from the old kmod system, we can't rename it.
kmod_info_t * kmod = NULL;

#define KEXT_PANICLIST_SIZE  (2 * PAGE_SIZE)


static char     * loaded_kext_paniclist         = NULL;
static uint32_t   loaded_kext_paniclist_size    = 0;

AbsoluteTime      last_loaded_timestamp;
static char       last_loaded_str_buf[2 * KMOD_MAX_NAME];
static u_long     last_loaded_strlen            = 0;
static void     * last_loaded_address           = NULL;
static u_long     last_loaded_size              = 0;

AbsoluteTime      last_unloaded_timestamp;
static char       last_unloaded_str_buf[2 * KMOD_MAX_NAME];
static u_long     last_unloaded_strlen          = 0;
static void     * last_unloaded_address         = NULL;
static u_long     last_unloaded_size            = 0;

// Statically linked kmods described by several mach-o sections:
//
// kPrelinkInfoSegment:kBuiltinInfoSection
// Array of pointers to kmod_info_t structs.
//
// kPrelinkInfoSegment:kBuiltinInfoSection
// Array of pointers to an embedded mach-o header.
//
// __DATA:kBuiltinInitSection, kBuiltinTermSection
// Structors for all kmods. Has to be filtered by proc address.
//

static uint32_t gBuiltinKmodsCount;
static kernel_section_t * gBuiltinKmodsSectionInfo;
static kernel_section_t * gBuiltinKmodsSectionStart;

const OSSymbol              * gIOSurfaceIdentifier;
vm_tag_t                      gIOSurfaceTag;

/*********************************************************************
 * sKextInnerLock protects against cross-calls with IOService and
 * IOCatalogue, and owns the variables declared immediately below.
 *
 * Note that sConsiderUnloadsExecuted above belongs to sKextLock!
 *
 * When both sKextLock and sKextInnerLock need to be taken,
 * always lock sKextLock first and unlock it second. Never take both
 * locks in an entry point to OSKext; if you need to do so, you must
 * spawn an independent thread to avoid potential deadlocks for threads
 * calling into OSKext.
 **********/
static IORecursiveLock *    sKextInnerLock             = NULL;

#if XNU_TARGET_OS_OSX
static bool                 sAutounloadEnabled         = true;
#endif
static bool                 sConsiderUnloadsCalled     = false;
static bool                 sConsiderUnloadsPending    = false;

static unsigned int         sConsiderUnloadDelay       = 60;     // seconds
static thread_call_t        sUnloadCallout             = NULL;
#if CONFIG_KXLD
static thread_call_t        sDestroyLinkContextThread  = NULL;   // one-shot, one-at-a-time thread
#endif // CONFIG_KXLD
static bool                 sSystemSleep               = false;  // true when system going to sleep
static AbsoluteTime         sLastWakeTime;                       // last time we woke up

/*********************************************************************
 * Backtraces can be printed at various times so we need a tight lock
 * on data used for that. sKextSummariesLock protects the variables
 * declared immediately below.
 *
 * gLoadedKextSummaries is accessed by other modules, but only during
 * a panic so the lock isn't needed then.
 *
 * gLoadedKextSummaries has the "used" attribute in order to ensure
 * that it remains visible even when we are performing extremely
 * aggressive optimizations, as it is needed to allow the debugger
 * to automatically parse the list of loaded kexts.
 **********/
static IOLock                 * sKextSummariesLock                = NULL;
extern "C" lck_grp_t            vm_page_lck_grp_bucket;
static lck_grp_t              * sKextAccountsLockGrp = &vm_page_lck_grp_bucket;
#define sKextAccountsLock       (&vm_allocation_sites_lock)

void(*const sLoadedKextSummariesUpdated)(void) = OSKextLoadedKextSummariesUpdated;
OSKextLoadedKextSummaryHeader * gLoadedKextSummaries __attribute__((used)) = NULL;
uint64_t gLoadedKextSummariesTimestamp __attribute__((used)) = 0;
static size_t sLoadedKextSummariesAllocSize = 0;

static OSKextActiveAccount    * sKextAccounts;
static uint32_t                 sKextAccountsCount;
};

/*********************************************************************
 * sKextLoggingLock protects the logging variables declared immediately below.
 **********/
__static_testable IOLock      * sKextLoggingLock           = NULL;

static  const OSKextLogSpec     kDefaultKernelLogFilter    = kOSKextLogBasicLevel |
    kOSKextLogVerboseFlagsMask;
static  OSKextLogSpec           sKernelLogFilter           = kDefaultKernelLogFilter;
static  bool                    sBootArgLogFilterFound     = false;
SYSCTL_UINT(_debug, OID_AUTO, kextlog, CTLFLAG_RW | CTLFLAG_LOCKED, &sKernelLogFilter,
    0, "kernel kext logging");

static  OSKextLogSpec           sUserSpaceKextLogFilter    = kOSKextLogSilentFilter;
static  OSSharedPtr<OSArray>    sUserSpaceLogSpecArray;
static  OSSharedPtr<OSArray>    sUserSpaceLogMessageArray;

/*********
 * End scope for sKextInnerLock-protected variables.
 *********************************************************************/

/*********************************************************************
 * OSValueObject concrete type instantiations
 **********/
OSDefineValueObjectForDependentType(void*)
OSDefineValueObjectForDependentType(OSKextRequestResourceCallback)


/**********************************************************************/

TUNABLE(uint32_t, kMaxDextCrashesInOneDay, "daily_max_dext_crashes", kMaxDextCrashesInOneDayDefault);

/*********************************************************************
 *  helper function used for collecting PGO data upon unload of a kext
 */

static int OSKextGrabPgoDataLocked(OSKext *kext,
    bool metadata,
    uuid_t instance_uuid,
    uint64_t *pSize,
    char *pBuffer,
    uint64_t bufferSize);

/**********************************************************************/



#if PRAGMA_MARK
#pragma mark OSData callbacks (need to move to OSData)
#endif
/*********************************************************************
* C functions used for callbacks.
*********************************************************************/
extern "C" {
void
osdata_kmem_free(void * ptr, unsigned int length)
{
	kmem_free(kernel_map, (vm_address_t)ptr, length);
	return;
}

void
osdata_phys_free(void * ptr, unsigned int length)
{
	ml_static_mfree((vm_offset_t)ptr, length);
	return;
}

void
osdata_vm_deallocate(void * ptr, unsigned int length)
{
	(void)vm_deallocate(kernel_map, (vm_offset_t)ptr, length);
	return;
}

void
osdata_kext_free(void * ptr, unsigned int length)
{
	(void)kext_free((vm_offset_t)ptr, length);
}
};

#if PRAGMA_MARK
#pragma mark KXLD Allocation Callback
#endif
#if CONFIG_KXLD
/*********************************************************************
* KXLD Allocation Callback
*********************************************************************/
kxld_addr_t
kern_allocate(
	u_long              size,
	KXLDAllocateFlags * flags,
	void              * user_data)
{
	vm_address_t  result       = 0; // returned
	kern_return_t mach_result  = KERN_FAILURE;
	bool          success      = false;
	OSKext      * theKext      = (OSKext *)user_data;
	unsigned int  roundSize    = 0;
	OSSharedPtr<OSData>      linkBuffer;

	if (round_page(size) > UINT_MAX) {
		OSKextLog(theKext,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "%s: Requested memory size is greater than UINT_MAX.",
		    theKext->getIdentifierCString());
		goto finish;
	}

	roundSize = (unsigned int)round_page(size);

	mach_result = kext_alloc(&result, roundSize, /* fixed */ FALSE);
	if (mach_result != KERN_SUCCESS) {
		OSKextLog(theKext,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "Can't allocate kernel memory to link %s.",
		    theKext->getIdentifierCString());
		goto finish;
	}

	/* Create an OSData wrapper for the allocated buffer.
	 */
	linkBuffer = OSData::withBytesNoCopy((void *)result, roundSize);
	if (!linkBuffer) {
		OSKextLog(theKext,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "Can't allocate linked executable wrapper for %s.",
		    theKext->getIdentifierCString());
		goto finish;
	}
	linkBuffer->setDeallocFunction(osdata_kext_free);
	OSKextLog(theKext,
	    kOSKextLogProgressLevel |
	    kOSKextLogLoadFlag | kOSKextLogLinkFlag,
	    "Allocated link buffer for kext %s at %p (%lu bytes).",
	    theKext->getIdentifierCString(),
	    (void *)result, (unsigned long)roundSize);

	theKext->setLinkedExecutable(linkBuffer.get());

	*flags = kKxldAllocateWritable;
	success = true;

finish:
	if (!success && result) {
		kext_free(result, roundSize);
		result = 0;
	}

	return (kxld_addr_t)result;
}

/*********************************************************************
*********************************************************************/
void
kxld_log_callback(
	KXLDLogSubsystem    subsystem,
	KXLDLogLevel        level,
	const char        * format,
	va_list             argList,
	void              * user_data)
{
	OSKext *theKext = (OSKext *) user_data;
	OSKextLogSpec logSpec = 0;

	switch (subsystem) {
	case kKxldLogLinking:
		logSpec |= kOSKextLogLinkFlag;
		break;
	case kKxldLogPatching:
		logSpec |= kOSKextLogPatchFlag;
		break;
	}

	switch (level) {
	case kKxldLogExplicit:
		logSpec |= kOSKextLogExplicitLevel;
		break;
	case kKxldLogErr:
		logSpec |= kOSKextLogErrorLevel;
		break;
	case kKxldLogWarn:
		logSpec |= kOSKextLogWarningLevel;
		break;
	case kKxldLogBasic:
		logSpec |= kOSKextLogProgressLevel;
		break;
	case kKxldLogDetail:
		logSpec |= kOSKextLogDetailLevel;
		break;
	case kKxldLogDebug:
		logSpec |= kOSKextLogDebugLevel;
		break;
	}

	OSKextVLog(theKext, logSpec, format, argList);
}
#endif // CONFIG_KXLD

#if PRAGMA_MARK
#pragma mark IOStatistics defines
#endif

#if IOKITSTATS

#define notifyKextLoadObservers(kext, kmod_info) \
do { \
    IOStatistics::onKextLoad(kext, kmod_info); \
} while (0)

#define notifyKextUnloadObservers(kext) \
do { \
    IOStatistics::onKextUnload(kext); \
} while (0)

#define notifyAddClassObservers(kext, addedClass, flags) \
do { \
    IOStatistics::onClassAdded(kext, addedClass); \
} while (0)

#define notifyRemoveClassObservers(kext, removedClass, flags) \
do { \
    IOStatistics::onClassRemoved(kext, removedClass); \
} while (0)

#else

#define notifyKextLoadObservers(kext, kmod_info)
#define notifyKextUnloadObservers(kext)
#define notifyAddClassObservers(kext, addedClass, flags)
#define notifyRemoveClassObservers(kext, removedClass, flags)

#endif /* IOKITSTATS */

#if PRAGMA_MARK
#pragma mark Module Config (Startup & Shutdown)
#endif
/*********************************************************************
* Module Config (Class Definition & Class Methods)
*********************************************************************/
#define super OSObject
OSDefineMetaClassAndStructors(OSKext, OSObject)

OSDefineMetaClassAndStructors(OSKextSavedMutableSegment, OSObject);

OSDefineMetaClassAndStructors(OSDextStatistics, OSObject);

/*********************************************************************
*********************************************************************/
/**
 * Allocate and intialize a fake/representative OSKext object for a statically
 * loaded (by iBoot) binary (e.g., the XNU kernel itself).
 *
 * @param kmod_info Pointer to the kmod_info structure for the binary being
 *                  setup. At least the "name" and "id" fields needs to already
 *                  be set correctly.
 *
 * @return The allocated and initialized OSKext object.
 */
/* static */
OSKext *
OSKext::allocAndInitFakeKext(kmod_info_t *kmod_info)
{
	vm_offset_t load_address = 0;
	const char *bundle_name = NULL;
	bool macho_is_unslid = false;
	bool set_custom_path = false;
	const char *executable_fallback_name = NULL;

	if (kmod_info->id == kOSKextKernelLoadTag) {
		load_address = (vm_offset_t)&_mh_execute_header;
		bundle_name = "mach_kernel";

		/* The kernel Mach-O header is fixed up to slide all of its addresses. */
		macho_is_unslid = false;

		/**
		 * No path to the binary is set for the kernel in its OSKext object. The
		 * kernel binary is located in fixed directories depending on the OS.
		 */
		set_custom_path = false;
		executable_fallback_name = NULL;
#if CONFIG_SPTM
	} else if (kmod_info->id == kOSKextSPTMLoadTag) {
		load_address = (vm_offset_t)SPTMArgs->debug_header->image[DEBUG_HEADER_ENTRY_SPTM];
		bundle_name = "sptm";

		/* The addresses in the SPTM Mach-O header are all unslid. */
		macho_is_unslid = true;

		set_custom_path = true;
		executable_fallback_name = "sptm.no.binname.in.macho";
	} else if (kmod_info->id == kOSKextTXMLoadTag) {
		load_address = (vm_offset_t)SPTMArgs->debug_header->image[DEBUG_HEADER_ENTRY_TXM];
		bundle_name = "txm";

		/* The addresses in the TXM Mach-O header are all unslid. */
		macho_is_unslid = true;

		set_custom_path = true;
		executable_fallback_name = "txm.no.binname.in.macho";
#endif /* CONFIG_SPTM */
	} else {
		panic("%s: Unsupported kmod_info->id (%d)", __func__, kmod_info->id);
	}

	/* Set up an OSKext instance to represent the statically loaded binary. */
	OSKext *fakeKext = new OSKext;
	assert(fakeKext);
	assert(load_address != 0);

	/*
	 * The start address is always a slid address whereas the last VA returned
	 * by getlastaddr() might be unslid depending on the Mach-O. If the address
	 * coming from the Mach-O is unslid, then unslide the start address before
	 * computing the length of the executable.
	 */
	size_t binaryLength = getlastaddr((kernel_mach_header_t*)load_address);
	binaryLength -= (macho_is_unslid) ? ml_static_unslide(load_address) : load_address;
	assert(binaryLength <= UINT_MAX);

	/**
	 * The load address is always slid. That value will be unslid before being
	 * exposed to userspace.
	 */
	OSSharedPtr<OSData> executable = OSData::withBytesNoCopy(
		(void*)load_address, (unsigned int)binaryLength);
	assert(executable);

	fakeKext->loadTag = sNextLoadTag++;
	fakeKext->bundleID = OSSymbol::withCString(kmod_info->name);

	fakeKext->version = OSKextParseVersionString(osrelease);
	fakeKext->compatibleVersion = fakeKext->version;
	fakeKext->linkedExecutable = os::move(executable);
	fakeKext->interfaceUUID = fakeKext->copyUUID();

	fakeKext->flags.hasAllDependencies = 1;
	fakeKext->flags.kernelComponent = 1;
	fakeKext->flags.prelinked = 0;
	fakeKext->flags.loaded = 1;
	fakeKext->flags.started = 1;
	fakeKext->flags.CPPInitialized = 0;
	fakeKext->flags.jettisonLinkeditSeg = 0;
	fakeKext->flags.unslidMachO = macho_is_unslid;

#if CONFIG_SPTM
	if (set_custom_path) {
		/* Only SPTM/TXM should have custom paths to their executables set. */
		assert((kmod_info->id == kOSKextSPTMLoadTag) ||
		    (kmod_info->id == kOSKextTXMLoadTag));

		/* All SPTM/TXM binaries are placed into the same path on internal systems. */
		fakeKext->path = OSString::withCStringNoCopy("/usr/appleinternal/standalone/platform");

		/**
		 * Each SPTM/TXM Mach-O should contain a __TEXT,__binname section which contains
		 * a character array representing the name of the Mach-O executable.
		 */
		kernel_section_t *binname_sect =
		    getsectbynamefromheader((kernel_mach_header_t*)load_address, "__TEXT", "__binname");

		if (binname_sect != NULL) {
			const char *binname = (const char *)ml_static_slide(binname_sect->addr);
			fakeKext->executableRelPath = OSString::withCStringNoCopy(binname);
		} else {
			fakeKext->executableRelPath = OSString::withCStringNoCopy(executable_fallback_name);
		}
	}
#endif /* CONFIG_SPTM */

	fakeKext->kmod_info = kmod_info;
	strlcpy(kmod_info->version, osrelease,
	    sizeof(kmod_info->version));
	kmod_info->size = binaryLength;
	assert(kmod_info->id == fakeKext->loadTag);

	/*
	 * Con up an info dict, so we don't have to have special-case checking all
	 * over.
	 */
	fakeKext->infoDict = OSDictionary::withCapacity(5);
	assert(fakeKext->infoDict);
	bool setResult = fakeKext->infoDict->setObject(kCFBundleIdentifierKey,
	    fakeKext->bundleID.get());
	assert(setResult);
	setResult = fakeKext->infoDict->setObject(kOSKernelResourceKey,
	    kOSBooleanTrue);
	assert(setResult);

	{
		OSSharedPtr<OSString> scratchString(OSString::withCStringNoCopy(osrelease));
		assert(scratchString);
		setResult = fakeKext->infoDict->setObject(kCFBundleVersionKey,
		    scratchString.get());
		assert(setResult);
	}

	{
		OSSharedPtr<OSString> scratchString(OSString::withCStringNoCopy(bundle_name));
		assert(scratchString);
		setResult = fakeKext->infoDict->setObject(kCFBundleNameKey,
		    scratchString.get());
		assert(setResult);
	}

	return fakeKext;
}

/* static */
void
OSKext::initialize(void)
{
	OSSharedPtr<OSData>     kernelExecutable   = NULL;// do not release
	IORegistryEntry * registryRoot       = NULL;// do not release
	OSSharedPtr<OSNumber> kernelCPUType;
	OSSharedPtr<OSNumber> kernelCPUSubtype;
	OSKextLogSpec     bootLogFilter      = kOSKextLogSilentFilter;
	bool              setResult          = false;
	uint64_t        * timestamp          = NULL;
	__unused char     bootArgBuffer[16];// for PE_parse_boot_argn w/strings

	/* This must be the first thing allocated. Everything else grabs this lock.
	 */
	sKextLock = IORecursiveLockAlloc();
	sKextInnerLock = IORecursiveLockAlloc();
	sKextSummariesLock = IOLockAlloc();
	sKextLoggingLock = IOLockAlloc();
	assert(sKextLock);
	assert(sKextInnerLock);
	assert(sKextSummariesLock);
	assert(sKextLoggingLock);

	sKextsByID = OSDictionary::withCapacity(kOSKextTypicalLoadCount);
	sLoadedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount);
	sLoadedDriverKitKexts = OSArray::withCapacity(kOSKextTypicalLoadCount);
	sUnloadedPrelinkedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount / 10);
	sKernelRequests = OSArray::withCapacity(0);
	sPostedKextLoadIdentifiers = OSSet::withCapacity(0);
	sAllKextLoadIdentifiers = OSSet::withCapacity(kOSKextTypicalLoadCount);
	sRequestCallbackRecords = OSArray::withCapacity(0);
	sDriverKitToUpgradeByID = OSDictionary::withCapacity(kOSKextTypicalUpgradeCount);

	assert(sKextsByID && sLoadedKexts && sLoadedDriverKitKexts && sKernelRequests &&
	    sPostedKextLoadIdentifiers && sAllKextLoadIdentifiers &&
	    sRequestCallbackRecords && sUnloadedPrelinkedKexts && sDriverKitToUpgradeByID);

	/* Read the log flag boot-args and set the log flags.
	 */
	if (PE_parse_boot_argn("kextlog", &bootLogFilter, sizeof(bootLogFilter))) {
		sBootArgLogFilterFound = true;
		sKernelLogFilter = bootLogFilter;
		// log this if any flags are set
		OSKextLog(/* kext */ NULL,
		    kOSKextLogBasicLevel |
		    kOSKextLogFlagsMask,
		    "Kernel kext log filter 0x%x per kextlog boot arg.",
		    (unsigned)sKernelLogFilter);
	}

#if !defined(__arm__) && !defined(__arm64__)
	/*
	 * On our ARM targets, the kernelcache/boot kernel collection contains
	 * the set of kexts required to boot, as specified by KCB.  Safeboot is
	 * either unsupported, or is supported by the bootloader only loading
	 * the boot kernel collection; as a result OSKext has no role to play
	 * in safeboot policy on ARM.
	 */
	sSafeBoot = PE_parse_boot_argn("-x", bootArgBuffer,
	    sizeof(bootArgBuffer)) ? true : false;
#endif /* defined(__arm__) && defined(__arm64__) */

	if (sSafeBoot) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogWarningLevel |
		    kOSKextLogGeneralFlag,
		    "SAFE BOOT DETECTED - "
		    "only valid OSBundleRequired kexts will be loaded.");
	}

	PE_parse_boot_argn("keepsyms", &sKeepSymbols, sizeof(sKeepSymbols));
#if CONFIG_DTRACE
	if (dtrace_keep_kernel_symbols()) {
		sKeepSymbols = true;
	}
#endif /* CONFIG_DTRACE */
#if KASAN_DYNAMIC_DENYLIST
	/* needed for function lookup */
	sKeepSymbols = true;
#endif /* KASAN_DYNAMIC_DENYLIST */

	/*
	 * Should we panic when the SystemKC is not linked against the
	 * BootKC that was loaded by the booter? By default: yes, if the
	 * "-nokcmismatchpanic" boot-arg is passed, then we _don't_ panic
	 * on mis-match and instead just print an error and continue.
	 */
	sPanicOnKCMismatch = PE_parse_boot_argn("-nokcmismatchpanic", bootArgBuffer,
	    sizeof(bootArgBuffer)) ? false : true;

	/* Set up an OSKext instance to represent the kernel itself. */
	sKernelKext = allocAndInitFakeKext(&g_kernel_kmod_info);
	assert(sKernelKext);

#if CONFIG_SPTM
	/* Set up OSKext instances to represent the SPTM/TXM. */
	OSKext *SPTMKext = allocAndInitFakeKext(&g_sptm_kmod_info);
	OSKext *TXMKext = allocAndInitFakeKext(&g_txm_kmod_info);
#endif

	/* Add the kernel kext to the bookkeeping dictionaries. Note that
	 * the kernel kext doesn't have a kmod_info struct. copyInfo()
	 * gathers info from other places anyhow.
	 */
	setResult = sKextsByID->setObject(sKernelKext->bundleID.get(), sKernelKext);
	assert(setResult);
	setResult = sLoadedKexts->setObject(sKernelKext);
	assert(setResult);

#if CONFIG_SPTM
	setResult = sKextsByID->setObject(SPTMKext->bundleID.get(), SPTMKext);
	assert(setResult);
	setResult = sLoadedKexts->setObject(SPTMKext);
	assert(setResult);

	setResult = sKextsByID->setObject(TXMKext->bundleID.get(), TXMKext);
	assert(setResult);
	setResult = sLoadedKexts->setObject(TXMKext);
	assert(setResult);
#endif /* CONFIG_SPTM */

	// XXX: better way with OSSharedPtr?
	// sKernelKext remains a valid pointer even after the decref
	sKernelKext->release();
#if CONFIG_SPTM
	SPTMKext->release();
	TXMKext->release();
#endif /* CONFIG_SPTM */

	registryRoot = IORegistryEntry::getRegistryRoot();
	kernelCPUType = OSNumber::withNumber(
		(long long unsigned int)_mh_execute_header.cputype,
		8 * sizeof(_mh_execute_header.cputype));
	kernelCPUSubtype = OSNumber::withNumber(
		(long long unsigned int)_mh_execute_header.cpusubtype,
		8 * sizeof(_mh_execute_header.cpusubtype));
	assert(registryRoot && kernelCPUSubtype && kernelCPUType);

	registryRoot->setProperty(kOSKernelCPUTypeKey, kernelCPUType.get());
	registryRoot->setProperty(kOSKernelCPUSubtypeKey, kernelCPUSubtype.get());

	gBuiltinKmodsSectionInfo = getsectbyname(kPrelinkInfoSegment, kBuiltinInfoSection);
	if (gBuiltinKmodsSectionInfo) {
		uint32_t count;

		assert(gBuiltinKmodsSectionInfo->addr);
		assert(gBuiltinKmodsSectionInfo->size);
		assert(gBuiltinKmodsSectionInfo->size / sizeof(kmod_info_t *) <= UINT_MAX);
		gBuiltinKmodsCount = (unsigned int)(gBuiltinKmodsSectionInfo->size / sizeof(kmod_info_t *));

		gBuiltinKmodsSectionStart = getsectbyname(kPrelinkInfoSegment, kBuiltinStartSection);
		assert(gBuiltinKmodsSectionStart);
		assert(gBuiltinKmodsSectionStart->addr);
		assert(gBuiltinKmodsSectionStart->size);
		assert(gBuiltinKmodsSectionStart->size / sizeof(uintptr_t) <= UINT_MAX);
		count = (unsigned int)(gBuiltinKmodsSectionStart->size / sizeof(uintptr_t));
		// one extra pointer for the end of last kmod
		assert(count == (gBuiltinKmodsCount + 1));

		vm_kernel_builtinkmod_text     = ((uintptr_t *)gBuiltinKmodsSectionStart->addr)[0];
		vm_kernel_builtinkmod_text_end = ((uintptr_t *)gBuiltinKmodsSectionStart->addr)[count - 1];
	}

	// Don't track this object -- it's never released
	gIOSurfaceIdentifier = OSSymbol::withCStringNoCopy("com.apple.iokit.IOSurface").detach();

	timestamp = __OSAbsoluteTimePtr(&last_loaded_timestamp);
	*timestamp = 0;
	timestamp = __OSAbsoluteTimePtr(&last_unloaded_timestamp);
	*timestamp = 0;
	timestamp = __OSAbsoluteTimePtr(&sLastWakeTime);
	*timestamp = 0;

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogGeneralFlag,
	    "Kext system initialized.");

	notifyKextLoadObservers(sKernelKext, sKernelKext->kmod_info);
#if CONFIG_SPTM
	notifyKextLoadObservers(SPTMKext, SPTMKext->kmod_info);
	notifyKextLoadObservers(TXMKext, TXMKext->kmod_info);
#endif

	return;
}

/*********************************************************************
* This is expected to be called exactly once, from exactly one thread
* context, during kernel bootstrap.
*********************************************************************/
/* static */
OSReturn
OSKext::removeKextBootstrap(void)
{
	OSReturn                   result                = kOSReturnError;

	const char               * dt_kernel_header_name = "Kernel-__HEADER";
	const char               * dt_kernel_symtab_name = "Kernel-__SYMTAB";
	kernel_mach_header_t     * dt_mach_header        = NULL;
	int                        dt_mach_header_size   = 0;
	struct symtab_command    * dt_symtab             = NULL;
	int                        dt_symtab_size        = 0;
	int                        dt_result             = 0;

	kernel_segment_command_t * seg_kld               = NULL;
	kernel_segment_command_t * seg_klddata           = NULL;
	kernel_segment_command_t * seg_linkedit          = NULL;

	const char __unused      * dt_segment_name       = NULL;
	void       __unused      * segment_paddress      = NULL;
	int        __unused        segment_size          = 0;

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogGeneralFlag,
	    "Jettisoning kext bootstrap segments.");

	/*
	 * keep the linkedit segment around when booted from a new MH_FILESET
	 * KC because all the kexts shared a linkedit segment.
	 */
	kc_format_t kc_format;
	if (!PE_get_primary_kc_format(&kc_format)) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "Unable to determine primary KC format");
	}

	/*****
	 * Dispose of unnecessary stuff that the booter didn't need to load.
	 */
	dt_result = IODTGetLoaderInfo(dt_kernel_header_name,
	    (void **)&dt_mach_header, &dt_mach_header_size);
	if (dt_result == 0 && dt_mach_header) {
		IODTFreeLoaderInfo(dt_kernel_header_name, (void *)dt_mach_header,
		    round_page_32(dt_mach_header_size));
	}
	dt_result = IODTGetLoaderInfo(dt_kernel_symtab_name,
	    (void **)&dt_symtab, &dt_symtab_size);
	if (dt_result == 0 && dt_symtab) {
		IODTFreeLoaderInfo(dt_kernel_symtab_name, (void *)dt_symtab,
		    round_page_32(dt_symtab_size));
	}

	/*****
	 * KLD & KLDDATA bootstrap segments.
	 */
	// xxx - should rename KLD segment
	seg_kld = getsegbyname("__KLD");
	seg_klddata = getsegbyname("__KLDDATA");
	if (seg_klddata) {
		// __mod_term_func is part of __KLDDATA
		OSRuntimeUnloadCPPForSegment(seg_klddata);
	}

#if __arm__ || __arm64__
	/* Free the memory that was set up by iBoot.
	 */
#if !defined(KERNEL_INTEGRITY_KTRR) && !defined(KERNEL_INTEGRITY_CTRR) && !defined(KERNEL_INTEGRITY_PV_CTRR)
	/* We cannot free the KLD segment with CTRR enabled as it contains text and
	 * is covered by the contiguous rorgn.
	 */
	dt_segment_name = "Kernel-__KLD";
	if (0 == IODTGetLoaderInfo(dt_segment_name, &segment_paddress, &segment_size)) {
		IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress,
		    (int)segment_size); // calls ml_static_mfree
	} else if (seg_kld && seg_kld->vmaddr && seg_kld->vmsize) {
		/* With fileset KCs, the Kernel KLD segment is not recorded in the DT. */
#if !CONFIG_SPTM
		ml_static_mfree(ml_static_ptovirt(seg_kld->vmaddr - gVirtBase + gPhysBase),
		    seg_kld->vmsize);
#else
		ml_static_mfree(seg_kld->vmaddr, seg_kld->vmsize);
#endif
	}
#endif
	dt_segment_name = "Kernel-__KLDDATA";
	if (0 == IODTGetLoaderInfo(dt_segment_name, &segment_paddress, &segment_size)) {
		IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress,
		    (int)segment_size);  // calls ml_static_mfree
	} else if (seg_klddata && seg_klddata->vmaddr && seg_klddata->vmsize) {
		/* With fileset KCs, the Kernel KLDDATA segment is not recorded in the DT. */
#if !CONFIG_SPTM
		ml_static_mfree(ml_static_ptovirt(seg_klddata->vmaddr - gVirtBase + gPhysBase),
		    seg_klddata->vmsize);
#else
		ml_static_mfree(seg_klddata->vmaddr, seg_klddata->vmsize);
#endif
	}
#elif __i386__ || __x86_64__
	/* On x86, use the mapping data from the segment load command to
	 * unload KLD & KLDDATA directly.
	 * This may invalidate any assumptions about  "avail_start"
	 * defining the lower bound for valid physical addresses.
	 */
	if (seg_kld && seg_kld->vmaddr && seg_kld->vmsize) {
		bzero((void *)seg_kld->vmaddr, seg_kld->vmsize);
		ml_static_mfree(seg_kld->vmaddr, seg_kld->vmsize);
	}
	if (seg_klddata && seg_klddata->vmaddr && seg_klddata->vmsize) {
		bzero((void *)seg_klddata->vmaddr, seg_klddata->vmsize);
		ml_static_mfree(seg_klddata->vmaddr, seg_klddata->vmsize);
	}
#else
#error arch
#endif

	/*****
	 * Prelinked kernel's symtab (if there is one).
	 */
	if (kc_format != KCFormatFileset) {
		kernel_section_t * sect;
		sect = getsectbyname("__PRELINK", "__symtab");
		if (sect && sect->addr && sect->size) {
			ml_static_mfree(sect->addr, sect->size);
		}
	}

	seg_linkedit = (kernel_segment_command_t *)getsegbyname("__LINKEDIT");

	/* kxld always needs the kernel's __LINKEDIT segment, but we can make it
	 * pageable, unless keepsyms is set.  To do that, we have to copy it from
	 * its booter-allocated memory, free the booter memory, reallocate proper
	 * managed memory, then copy the segment back in.
	 *
	 * NOTE: This optimization is not valid for fileset KCs because each
	 * fileset entry (kext or xnu) in an MH_FILESET has a LINKEDIT segment
	 * that points to one fileset-global LINKEDIT segment. This
	 * optimization is also only valid for platforms that support vm
	 * mapped kexts or mapped kext collections (pageable KCs)
	 */
#if VM_MAPPED_KEXTS
	if (!sKeepSymbols && kc_format != KCFormatFileset) {
		kern_return_t mem_result;
		void *seg_copy = NULL;
		void *seg_data = NULL;
		vm_map_offset_t seg_offset = 0;
		vm_map_offset_t seg_copy_offset = 0;
		vm_map_size_t seg_length = 0;

		seg_data = (void *) seg_linkedit->vmaddr;
		seg_offset = (vm_map_offset_t) seg_linkedit->vmaddr;
		seg_length = (vm_map_size_t) seg_linkedit->vmsize;

		/* Allocate space for the LINKEDIT copy.
		 */
		mem_result = kmem_alloc(kernel_map, (vm_offset_t *) &seg_copy,
		    seg_length, KMA_ZERO, VM_KERN_MEMORY_KEXT);
		if (mem_result != KERN_SUCCESS) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
			    "Can't copy __LINKEDIT segment for VM reassign.");
			return result;
		}
		seg_copy_offset = (vm_map_offset_t) seg_copy;

		/* Copy it out.
		 */
		memcpy(seg_copy, seg_data, seg_length);

		/* Dump the booter memory.
		 */
		ml_static_mfree(seg_offset, seg_length);

		/* Set up the VM region.
		 */
		mem_result = mach_vm_map_kernel(
			kernel_map,
			&seg_offset,
			seg_length, /* mask */ 0,
			VM_MAP_KERNEL_FLAGS_FIXED(.vmf_overwrite = true),
			(ipc_port_t)NULL,
			(vm_object_offset_t) 0,
			/* copy */ FALSE,
			/* cur_protection */ VM_PROT_READ | VM_PROT_WRITE,
			/* max_protection */ VM_PROT_ALL,
			/* inheritance */ VM_INHERIT_DEFAULT);
		if ((mem_result != KERN_SUCCESS) ||
		    (seg_offset != (vm_map_offset_t) seg_data)) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
			    "Can't create __LINKEDIT VM entry at %p, length 0x%llx (error 0x%x).",
			    seg_data, seg_length, mem_result);
			return result;
		}

		/* And copy it back.
		 */
		memcpy(seg_data, seg_copy, seg_length);

		/* Free the copy.
		 */
		kmem_free(kernel_map, seg_copy_offset, seg_length);
	} else if (!sKeepSymbols && kc_format == KCFormatFileset) {
		/* Remove the linkedit segment of the Boot KC */
		kernel_mach_header_t *mh = (kernel_mach_header_t *)PE_get_kc_header(KCKindPrimary);
		OSKext::jettisonFileSetLinkeditSegment(mh);
	}
#else // !VM_MAPPED_KEXTS
	/*****
	 * Dump the LINKEDIT segment, unless keepsyms is set.
	 */
	if (!sKeepSymbols && kc_format != KCFormatFileset) {
		dt_segment_name = "Kernel-__LINKEDIT";
		if (0 == IODTGetLoaderInfo(dt_segment_name,
		    &segment_paddress, &segment_size)) {
#ifdef SECURE_KERNEL
			vm_offset_t vmaddr = ml_static_ptovirt((vm_offset_t)segment_paddress);
			bzero((void*)vmaddr, segment_size);
#endif
			IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress,
			    (int)segment_size);
		}
	} else if (!sKeepSymbols && kc_format == KCFormatFileset) {
		/* Remove the linkedit segment of the Boot KC */
		kernel_mach_header_t *mh = (kernel_mach_header_t *)PE_get_kc_header(KCKindPrimary);
		OSKext::jettisonFileSetLinkeditSegment(mh);
	} else {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogBasicLevel |
		    kOSKextLogGeneralFlag,
		    "keepsyms boot arg specified; keeping linkedit segment for symbols.");
	}
#endif // VM_MAPPED_KEXTS

	result = kOSReturnSuccess;

	return result;
}

#if CONFIG_KXLD
/*********************************************************************
*********************************************************************/
void
OSKext::flushNonloadedKexts(
	Boolean flushPrelinkedKexts)
{
	OSSharedPtr<OSSet>                keepKexts;

	/* TODO: make this more efficient with MH_FILESET kexts */

	// Do not unload prelinked kexts on arm because the kernelcache is not
	// structured in a way that allows them to be unmapped
#if !defined(__x86_64__)
	flushPrelinkedKexts = false;
#endif /* defined(__x86_64__) */

	IORecursiveLockLock(sKextLock);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogKextBookkeepingFlag,
	    "Flushing nonloaded kexts and other unused data.");

	OSKext::considerDestroyingLinkContext();

	/* If we aren't flushing unused prelinked kexts, we have to put them
	 * aside while we flush everything else so make a container for them.
	 */
	keepKexts = OSSet::withCapacity(16);
	if (!keepKexts) {
		goto finish;
	}

	/* Set aside prelinked kexts (in-use or not) and break
	 * any lingering inter-kext references for nonloaded kexts
	 * so they have min. retain counts.
	 */
	{
		sKextsByID->iterateObjects(^bool (const OSSymbol * thisID __unused, OSObject * obj) {
			OSKext * thisKext = OSDynamicCast(OSKext, obj);
			if (!thisKext) {
			        return false;
			}
			if (!flushPrelinkedKexts && thisKext->isPrelinked()) {
			        keepKexts->setObject(thisKext);
			} else if (!thisKext->declaresExecutable()) {
			        /*
			         * Don't unload codeless kexts, because they never appear in the loadedKexts array.
			         * Requesting one from the IOKit daemon will load it and then immediately remove it by calling
			         * flushNonloadedKexts().
			         * And adding one to loadedKexts breaks code assuming they have kmod_info etc.
			         */
			        keepKexts->setObject(thisKext);
			} else if (thisKext->isInFileset()) {
			        /* keep all kexts in the new MH_FILESET KC */
			        keepKexts->setObject(thisKext);
			}

			thisKext->flushDependencies(/* forceIfLoaded */ false);
			return false;
		});
	}
	/* Dump all the kexts in the ID dictionary; we'll repopulate it shortly.
	 */
	sKextsByID->flushCollection();

	/* Now put the loaded kexts back into the ID dictionary.
	 */
	sLoadedKexts->iterateObjects(^bool (OSObject * obj) {
		OSKext * thisKext = OSDynamicCast(OSKext, obj);
		if (!thisKext) {
		        return false;
		}
		sKextsByID->setObject(thisKext->getIdentifierCString(), thisKext);
		return false;
	});

	/* Finally, put back the kept kexts if we saved any.
	 */
	keepKexts->iterateObjects(^bool (OSObject * obj) {
		OSKext * thisKext = OSDynamicCast(OSKext, obj);
		if (!thisKext) {
		        return false;
		}
		sKextsByID->setObject(thisKext->getIdentifierCString(), thisKext);
		return false;
	});

finish:
	IORecursiveLockUnlock(sKextLock);
	return;
}
#else /* !CONFIG_KXLD */

void
OSKext::flushNonloadedKexts(
	Boolean flushPrelinkedKexts __unused)
{
	IORecursiveLockLock(sKextLock);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogKextBookkeepingFlag,
	    "Flushing dependency info for non-loaded kexts.");

	/*
	 * In a world where we don't dynamically link kexts, they all come
	 * from a kext collection that's either in wired memory, or
	 * wire-on-demand. We don't need to mess around with moving kexts in
	 * and out of the sKextsByID array - they can all just stay there.
	 * Here we just flush the dependency list for kexts that are not
	 * loaded.
	 */
	sKextsByID->iterateObjects(^bool (const OSSymbol * thisID __unused, OSObject * obj) {
		OSKext * thisKext = OSDynamicCast(OSKext, obj);
		if (!thisKext) {
		        return false;
		}
		thisKext->flushDependencies(/* forceIfLoaded */ false);
		return false;
	});

	IORecursiveLockUnlock(sKextLock);
	return;
}

#endif /* CONFIG_KXLD */

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::setIOKitDaemonActive(bool active)
{
	IOServiceTrace(IOSERVICE_KEXTD_ALIVE, 0, 0, 0, 0);
	IORecursiveLockLock(sKextLock);
	sIOKitDaemonActive = active;
	if (sKernelRequests->getCount()) {
		OSKext::pingIOKitDaemon();
	}
	IORecursiveLockUnlock(sKextLock);

	return;
}

/*********************************************************************
* OSKextLib.cpp might need access to this someday but for now it's
* private.
*********************************************************************/
extern "C" {
extern void ipc_port_release_send(ipc_port_t);
};

/* static */
OSReturn
OSKext::pingIOKitDaemon(void)
{
	OSReturn    result     = kOSReturnError;
#if !NO_KEXTD
	mach_port_t kextd_port = IPC_PORT_NULL;

	if (!sIOKitDaemonActive) {
		result = kOSKextReturnDisabled; // basically unavailable
		goto finish;
	}

	result = host_get_kextd_port(host_priv_self(), &kextd_port);
	if (result != KERN_SUCCESS || !IPC_PORT_VALID(kextd_port)) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Can't get " kIOKitDaemonName " port.");
		goto finish;
	}

	result = kextd_ping(kextd_port);
	if (result != KERN_SUCCESS) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    kIOKitDaemonName " ping failed (0x%x).", (int)result);
		goto finish;
	}

finish:
	if (IPC_PORT_VALID(kextd_port)) {
		ipc_port_release_send(kextd_port);
	}
#endif

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */

bool
OSKext::driverkitEnabled(void)
{
	#if XNU_TARGET_OS_WATCH
	/*
	 * Driverkit support is available on watchOS only if the device
	 * tree has the "supports-driverkit" property in its "/product" node
	 */
	DTEntry entry;
	void const *prop = NULL;
	unsigned int prop_size;

	if (kSuccess != SecureDTLookupEntry(NULL, "/product", &entry)) {
		return false;
	}
	if (kSuccess != SecureDTGetProperty(entry, "supports-driverkit", &prop, &prop_size)) {
		return false;
	}
	#endif /* XNU_TARGET_OS_WATCH */

	return true;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::iokitDaemonAvailable(void)
{
#if !XNU_TARGET_OS_IOS && !XNU_TARGET_OS_OSX && !XNU_TARGET_OS_WATCH
	int notused;
	if (PE_parse_boot_argn("-restore", &notused, sizeof(notused))) {
		return false;
	}
#endif /* !XNU_TARGET_OS_IOS && !XNU_TARGET_OS_OSX && !XNU_TARGET_OS_WATCH */
	return driverkitEnabled();
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::setDeferredLoadSucceeded(Boolean succeeded)
{
	IORecursiveLockLock(sKextLock);
	sDeferredLoadSucceeded = succeeded;
	IORecursiveLockUnlock(sKextLock);

	return;
}

/*********************************************************************
* Called from IOSystemShutdownNotification.
*********************************************************************/
/* static */
void
OSKext::willShutdown(void)
{
#if !NO_KEXTD
	OSReturn       checkResult = kOSReturnError;
#endif
	OSSharedPtr<OSDictionary> exitRequest;

	IORecursiveLockLock(sKextLock);

	OSKext::setLoadEnabled(false);
	OSKext::setUnloadEnabled(false);
	OSKext::setAutounloadsEnabled(false);
	OSKext::setKernelRequestsEnabled(false);

#if defined(__x86_64__) || defined(__i386__)
	if (IOPMRootDomainGetWillShutdown()) {
		OSKext::freeKCFileSetcontrol();
	}
#endif // (__x86_64__) || defined(__i386__)

#if !NO_KEXTD
	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogGeneralFlag,
	    "System shutdown; requesting immediate " kIOKitDaemonName " exit.");

	checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestDaemonExit,
	    exitRequest);
	if (checkResult != kOSReturnSuccess) {
		goto finish;
	}
	if (!sKernelRequests->setObject(exitRequest.get())) {
		goto finish;
	}

	OSKext::pingIOKitDaemon();

finish:
#endif

	IORecursiveLockUnlock(sKextLock);
	return;
}

void
OSKext::setWillUserspaceReboot(void)
{
	OSKext::willShutdown();
	IOService::setWillUserspaceReboot();
	gIOCatalogue->terminateDriversForUserspaceReboot();
}

void
OSKext::resetAfterUserspaceReboot(void)
{
	OSSharedPtr<OSArray> arr = OSArray::withCapacity(1);
	IOService::updateConsoleUsers(arr.get(), 0, true /* after_userspace_reboot */);

	IORecursiveLockLock(sKextLock);
	gIOCatalogue->resetAfterUserspaceReboot();
	IOService::userSpaceDidReboot();
	OSKext::removeDaemonExitRequests();
	OSKext::setLoadEnabled(true);
	OSKext::setUnloadEnabled(true);
	OSKext::setAutounloadsEnabled(true);
	OSKext::setKernelRequestsEnabled(true);
	sOSKextWasResetAfterUserspaceReboot = true;
	IORecursiveLockUnlock(sKextLock);
}

extern "C" int
OSKextIsInUserspaceReboot(void)
{
	return IOService::getWillUserspaceReboot();
}

extern "C" void
OSKextResetAfterUserspaceReboot(void)
{
	OSKext::resetAfterUserspaceReboot();
}

/*
 * Remove daemon exit requests from sKernelRequests
 *
 * If we sent a daemon exit request during a userspace reboot and launchd
 * killed the IOKit daemon before it was able to dequeue the exit request, the
 * next time the daemon starts up it will immediately exit as it gets the old exit request.
 *
 * This removes exit requests so that this does not happen.
 */
void
OSKext::removeDaemonExitRequests(void)
{
	OSDictionary * current = NULL;
	OSString     * predicate = NULL;
	size_t         index     = 0;
	OSSharedPtr<const OSSymbol> predicateKey = OSSymbol::withCString(kKextRequestPredicateKey);

	while (index < sKernelRequests->getCount()) {
		current = OSDynamicCast(OSDictionary, sKernelRequests->getObject(index));
		if (current) {
			predicate = OSDynamicCast(OSString, current->getObject(predicateKey.get()));
			if (predicate && predicate->isEqualTo(kKextRequestPredicateRequestDaemonExit)) {
				sKernelRequests->removeObject(index);
				continue;
			}
		}
		index++;
	}
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::getLoadEnabled(void)
{
	bool result;

	IORecursiveLockLock(sKextLock);
	result = sLoadEnabled;
	IORecursiveLockUnlock(sKextLock);
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::setLoadEnabled(bool flag)
{
	bool result;

	IORecursiveLockLock(sKextLock);
	result = sLoadEnabled;
	sLoadEnabled = (flag ? true : false);

	if (sLoadEnabled != result) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogBasicLevel |
		    kOSKextLogLoadFlag,
		    "Kext loading now %sabled.", sLoadEnabled ? "en" : "dis");
	}

	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::getUnloadEnabled(void)
{
	bool result;

	IORecursiveLockLock(sKextLock);
	result = sUnloadEnabled;
	IORecursiveLockUnlock(sKextLock);
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::setUnloadEnabled(bool flag)
{
	bool result;

	IORecursiveLockLock(sKextLock);
	result = sUnloadEnabled;
	sUnloadEnabled = (flag ? true : false);
	IORecursiveLockUnlock(sKextLock);

	if (sUnloadEnabled != result) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogBasicLevel |
		    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
		    "Kext unloading now %sabled.", sUnloadEnabled ? "en" : "dis");
	}

	return result;
}

/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
bool
OSKext::getAutounloadEnabled(void)
{
#if XNU_TARGET_OS_OSX
	bool result;

	IORecursiveLockLock(sKextInnerLock);
	result = sAutounloadEnabled ? true : false;
	IORecursiveLockUnlock(sKextInnerLock);
	return result;
#else
	return false;
#endif /* XNU_TARGET_OS_OSX */
}

/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
bool
OSKext::setAutounloadsEnabled(bool flag)
{
#if XNU_TARGET_OS_OSX
	bool result;

	IORecursiveLockLock(sKextInnerLock);

	result = sAutounloadEnabled;
	sAutounloadEnabled = (flag ? true : false);
	if (!sAutounloadEnabled && sUnloadCallout) {
		thread_call_cancel(sUnloadCallout);
	}

	if (sAutounloadEnabled != result) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogBasicLevel |
		    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
		    "Kext autounloading now %sabled.",
		    sAutounloadEnabled ? "en" : "dis");
	}

	IORecursiveLockUnlock(sKextInnerLock);

	return result;
#else
	(void)flag;
	return false;
#endif /* XNU_TARGET_OS_OSX */
}

/*********************************************************************
*********************************************************************/
/* instance method operating on OSKext field */
bool
OSKext::setAutounloadEnabled(bool flag)
{
	bool result = flags.autounloadEnabled ? true : false;
	flags.autounloadEnabled = flag ? (0 == flags.unloadUnsupported) : 0;

	if (result != (flag ? true : false)) {
		OSKextLog(this,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Autounloading for kext %s now %sabled.",
		    getIdentifierCString(),
		    flags.autounloadEnabled ? "en" : "dis");
	}
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::setKernelRequestsEnabled(bool flag)
{
	bool result;

	IORecursiveLockLock(sKextLock);
	result = sKernelRequestsEnabled;
	sKernelRequestsEnabled = flag ? true : false;

	if (sKernelRequestsEnabled != result) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogBasicLevel |
		    kOSKextLogGeneralFlag,
		    "Kernel requests now %sabled.",
		    sKernelRequestsEnabled ? "en" : "dis");
	}
	IORecursiveLockUnlock(sKextLock);
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::getKernelRequestsEnabled(void)
{
	bool result;

	IORecursiveLockLock(sKextLock);
	result = sKernelRequestsEnabled;
	IORecursiveLockUnlock(sKextLock);
	return result;
}

static bool
segmentIsMutable(kernel_segment_command_t *seg)
{
	/* Mutable segments have to have VM_PROT_WRITE */
	if ((seg->maxprot & VM_PROT_WRITE) == 0) {
		return false;
	}
	/* Exclude the __DATA_CONST segment */
	if (strncmp(seg->segname, "__DATA_CONST", sizeof(seg->segname)) == 0) {
		return false;
	}
	/* Exclude __LINKEDIT */
	if (strncmp(seg->segname, "__LINKEDIT", sizeof(seg->segname)) == 0) {
		return false;
	}
	return true;
}

#if PRAGMA_MARK
#pragma mark Kext Life Cycle
#endif
/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::withPrelinkedInfoDict(
	OSDictionary * anInfoDict,
	bool doCoalescedSlides,
	kc_kind_t type)
{
	OSSharedPtr<OSKext> newKext(OSMakeShared<OSKext>());

	if (newKext && !newKext->initWithPrelinkedInfoDict(anInfoDict, doCoalescedSlides, type)) {
		return NULL;
	}

	return newKext;
}

OSData *
OSKext::parseDextUniqueID(
	OSDictionary * anInfoDict,
	const char *dextIDCS)
{
	OSData *ret = NULL;
	OSData *data_duid = OSDynamicCast(OSData, anInfoDict->getObject(kOSBundleDextUniqueIdentifierKey));
	if (data_duid != NULL) {
		if (data_duid->getLength() > KOSBundleDextUniqueIdentifierMaxLength) {
			OSKextLog(NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Dext %s DextUniqueIdentifier too long.",
			    dextIDCS);
		} else {
			/*
			 * If the DextUniqueID exists it should be
			 * present also into the personalities.
			 */
			setDextUniqueIDInPersonalities(anInfoDict, data_duid);
			ret = data_duid;
		}
	} else {
		OSKextLog(NULL,
		    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
		    "Dext %s does not have a DextUniqueIdentifier",
		    dextIDCS);
	}
	return ret;
}

void
OSKext::setDextUniqueIDInPersonalities(
	OSDictionary * anInfoDict,
	OSData * dextUniqueID)
{
	OSDictionary * dextPersonalities = NULL;
	OSSharedPtr<OSCollectionIterator> personalitiesIterator;
	OSString * personalityName = NULL;

	dextPersonalities = OSDynamicCast(OSDictionary,
	    anInfoDict->getObject(kIOKitPersonalitiesKey));
	if (!dextPersonalities || !dextPersonalities->getCount()) {
		return;
	}

	personalitiesIterator =
	    OSCollectionIterator::withCollection(dextPersonalities);
	if (!personalitiesIterator) {
		return;
	}
	while ((personalityName = OSDynamicCast(OSString,
	    personalitiesIterator->getNextObject()))) {
		OSDictionary * personality = OSDynamicCast(OSDictionary,
		    dextPersonalities->getObject(personalityName));
		if (personality) {
			OSObject *duid = personality->getObject(kOSBundleDextUniqueIdentifierKey);
			if (duid == NULL) {
				personality->setObject(kOSBundleDextUniqueIdentifierKey, dextUniqueID);
			}
		}
	}
}
/*********************************************************************
*********************************************************************/
bool
OSKext::initWithPrelinkedInfoDict(
	OSDictionary * anInfoDict,
	bool doCoalescedSlides,
	kc_kind_t type)
{
	bool            result              = false;
	OSString      * kextPath            = NULL;                // do not release
	OSNumber      * addressNum          = NULL;                // reused; do not release
	OSNumber      * lengthNum           = NULL;                // reused; do not release
	OSBoolean     * scratchBool         = NULL;                // do not release
	void          * data                = NULL;                // do not free
	void          * srcData             = NULL;                // do not free
	OSSharedPtr<OSData>        prelinkedExecutable;
	uint32_t        length              = 0;                // reused
	uintptr_t       kext_slide          = PE_get_kc_slide(type);
	bool            shouldSaveSegments  = false;
	kc_format     format                = KCFormatUnknown;

	if (!super::init()) {
		goto finish;
	}

	/* Get the path. Don't look for an arch-specific path property.
	 */
	kextPath = OSDynamicCast(OSString,
	    anInfoDict->getObject(kPrelinkBundlePathKey));

	if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
		goto finish;
	}

#if KASLR_KEXT_DEBUG
	IOLog("kaslr: doCoalescedSlides %d kext %s \n", doCoalescedSlides, getIdentifierCString());
#endif

	/* Also get the executable's bundle-relative path if present.
	 * Don't look for an arch-specific path property.
	 */
	executableRelPath.reset(OSDynamicCast(OSString,
	    anInfoDict->getObject(kPrelinkExecutableRelativePathKey)), OSRetain);
	userExecutableRelPath.reset(OSDynamicCast(OSString,
	    anInfoDict->getObject(kCFBundleDriverKitExecutableKey)), OSRetain);

	/* Don't need the paths to be in the info dictionary any more.
	 */
	anInfoDict->removeObject(kPrelinkBundlePathKey);
	anInfoDict->removeObject(kPrelinkExecutableRelativePathKey);

	scratchBool = OSDynamicCast(OSBoolean,
	    getPropertyForHostArch(kOSBundleRequireExplicitLoadKey));
	if (scratchBool == kOSBooleanTrue) {
		flags.requireExplicitLoad = 1;
	}

	/* Create an OSData wrapper around the linked executable.
	 */
	addressNum = OSDynamicCast(OSNumber,
	    anInfoDict->getObject(kPrelinkExecutableLoadKey));
	if (addressNum && addressNum->unsigned64BitValue() != kOSKextCodelessKextLoadAddr) {
		lengthNum = OSDynamicCast(OSNumber,
		    anInfoDict->getObject(kPrelinkExecutableSizeKey));
		if (!lengthNum) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Kext %s can't find prelinked kext executable size.",
			    getIdentifierCString());
			return result;
		}

		data = (void *) (((uintptr_t) (addressNum->unsigned64BitValue())) + kext_slide);
		length = (uint32_t) (lengthNum->unsigned32BitValue());

#if KASLR_KEXT_DEBUG
		IOLog("kaslr: unslid 0x%lx slid 0x%lx length %u - prelink executable \n",
		    (unsigned long)ml_static_unslide((vm_offset_t)data),
		    (unsigned long)data,
		    length);
#endif

		anInfoDict->removeObject(kPrelinkExecutableLoadKey);
		anInfoDict->removeObject(kPrelinkExecutableSizeKey);

		/* If the kext's load address differs from its source address, allocate
		 * space in the kext map at the load address and copy the kext over.
		 */
		addressNum = OSDynamicCast(OSNumber, anInfoDict->getObject(kPrelinkExecutableSourceKey));
		if (addressNum) {
			srcData = (void *) (((uintptr_t) (addressNum->unsigned64BitValue())) + kext_slide);

#if KASLR_KEXT_DEBUG
			IOLog("kaslr: unslid 0x%lx slid 0x%lx - prelink executable source \n",
			    (unsigned long)ml_static_unslide((vm_offset_t)srcData),
			    (unsigned long)srcData);
#endif

			if (data != srcData) {
#if __LP64__
				kern_return_t alloc_result;

				alloc_result = kext_alloc((vm_offset_t *)&data, length, /* fixed */ TRUE);
				if (alloc_result != KERN_SUCCESS) {
					OSKextLog(this,
					    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
					    "Failed to allocate space for prelinked kext %s.",
					    getIdentifierCString());
					goto finish;
				}
				memcpy(data, srcData, length);
#else
				OSKextLog(this,
				    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
				    "Error: prelinked kext %s - source and load addresses "
				    "differ on ILP32 architecture.",
				    getIdentifierCString());
				goto finish;
#endif /* __LP64__ */
			}

			anInfoDict->removeObject(kPrelinkExecutableSourceKey);
		}

		prelinkedExecutable = OSData::withBytesNoCopy(data, length);
		if (!prelinkedExecutable) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag | kOSKextLogArchiveFlag,
			    "Kext %s failed to create executable wrapper.",
			    getIdentifierCString());
			goto finish;
		}

		/*
		 * Fileset KCs are mapped as a whole by iBoot.
		 * Individual kext executables should not be unmapped
		 * by xnu.
		 * Doing so may result in panics like rdar://85419651
		 */
		if (PE_get_kc_format(kc_type, &format) && (format == KCFormatFileset)) {
			prelinkedExecutable->setDeallocFunction(NULL);
		} else { // Not from a Fileset KC
#if VM_MAPPED_KEXTS
			prelinkedExecutable->setDeallocFunction(osdata_kext_free);
#else
			prelinkedExecutable->setDeallocFunction(osdata_phys_free);
#endif
		}
		setLinkedExecutable(prelinkedExecutable.get());
		addressNum = OSDynamicCast(OSNumber,
		    anInfoDict->getObject(kPrelinkKmodInfoKey));
		if (!addressNum) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Kext %s can't find prelinked kext kmod_info address.",
			    getIdentifierCString());
			goto finish;
		}

		if (addressNum->unsigned64BitValue() != 0) {
			kmod_info = (kmod_info_t *) (((uintptr_t) (addressNum->unsigned64BitValue())) + kext_slide);
			if (kmod_info->address) {
				kmod_info->address = (((uintptr_t)(kmod_info->address)) + kext_slide);
			} else {
				kmod_info->address = (uintptr_t)data;
				kmod_info->size = length;
			}
#if KASLR_KEXT_DEBUG
			IOLog("kaslr: unslid 0x%lx slid 0x%lx - kmod_info \n",
			    (unsigned long)((vm_offset_t)kmod_info) - kext_slide,
			    (unsigned long)kmod_info);
			IOLog("kaslr: unslid 0x%lx slid 0x%lx - kmod_info->address \n",
			    (unsigned long)((vm_offset_t)kmod_info->address) - kext_slide,
			    (unsigned long)kmod_info->address);
 #endif
		}

		anInfoDict->removeObject(kPrelinkKmodInfoKey);
	}

	if ((addressNum = OSDynamicCast(OSNumber, anInfoDict->getObject("ModuleIndex")))) {
		uintptr_t builtinTextStart;
		uintptr_t builtinTextEnd;

		flags.builtin = true;
		builtinKmodIdx = addressNum->unsigned32BitValue();
		assert(builtinKmodIdx < gBuiltinKmodsCount);

		builtinTextStart = ((uintptr_t *)gBuiltinKmodsSectionStart->addr)[builtinKmodIdx];
		builtinTextEnd   = ((uintptr_t *)gBuiltinKmodsSectionStart->addr)[builtinKmodIdx + 1];

		kmod_info = ((kmod_info_t **)gBuiltinKmodsSectionInfo->addr)[builtinKmodIdx];
		kmod_info->address = builtinTextStart;
		kmod_info->size    = builtinTextEnd - builtinTextStart;
	}

	/* If the plist has a UUID for an interface, save that off.
	 */
	if (isInterface()) {
		interfaceUUID.reset(OSDynamicCast(OSData,
		    anInfoDict->getObject(kPrelinkInterfaceUUIDKey)), OSRetain);
		if (interfaceUUID) {
			anInfoDict->removeObject(kPrelinkInterfaceUUIDKey);
		}
	}

	result = (kOSReturnSuccess == slidePrelinkedExecutable(doCoalescedSlides));
	if (!result) {
		goto finish;
	}

	kc_type = type;
	/* Exclude builtin and codeless kexts */
	if (prelinkedExecutable && kmod_info) {
		switch (kc_type) {
		case KCKindPrimary:
			shouldSaveSegments = (
				getPropertyForHostArch(kOSMutableSegmentCopy) == kOSBooleanTrue ||
				getPropertyForHostArch(kOSBundleAllowUserLoadKey) == kOSBooleanTrue);
			if (shouldSaveSegments) {
				flags.resetSegmentsFromImmutableCopy = 1;
			} else {
				flags.unloadUnsupported = 1;
			}
			break;
		case KCKindPageable:
			flags.resetSegmentsFromVnode = 1;
			break;
		case KCKindAuxiliary:
			if (!pageableKCloaded) {
				flags.resetSegmentsFromImmutableCopy = 1;
			} else if (resetAuxKCSegmentOnUnload) {
				flags.resetSegmentsFromVnode = 1;
			} else {
				flags.unloadUnsupported = 1;
			}
			break;
		default:
			break;
		}
	}

	if (flags.resetSegmentsFromImmutableCopy) {
		/* Save a pristine copy of the mutable segments */
		kernel_segment_command_t *seg = NULL;
		kernel_mach_header_t *k_mh = (kernel_mach_header_t *)kmod_info->address;

		savedMutableSegments = OSArray::withCapacity(0);

		for (seg = firstsegfromheader(k_mh); seg; seg = nextsegfromheader(k_mh, seg)) {
			if (!segmentIsMutable(seg)) {
				continue;
			}
			uint64_t unslid_vmaddr = seg->vmaddr - kext_slide;
			uint64_t vmsize = seg->vmsize;
			OSKextLog(this, kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "Saving kext %s mutable segment %.*s %llx->%llx.", getIdentifierCString(), (int)strnlen(seg->segname, sizeof(seg->segname)), seg->segname, unslid_vmaddr, unslid_vmaddr + vmsize - 1);
			OSSharedPtr<OSKextSavedMutableSegment> savedSegment = OSKextSavedMutableSegment::withSegment(seg);
			if (!savedSegment) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogGeneralFlag,
				    "Kext %s failed to save mutable segment %llx->%llx.", getIdentifierCString(), unslid_vmaddr, unslid_vmaddr + vmsize - 1);
				result = false;
				goto finish;
			}
			savedMutableSegments->setObject(savedSegment);
		}
	}

	if (doCoalescedSlides == false && !flags.resetSegmentsFromVnode) {
		/*
		 * set VM protections now, wire pages for the old style Aux KC now,
		 * wire pages for the rest of the KC types at load time.
		 */
		result = (kOSReturnSuccess == setVMAttributes(true, (type == KCKindAuxiliary) ? true : false));
		if (!result) {
			goto finish;
		}
	}

	flags.prelinked = true;

	if (isDriverKit()) {
		dextStatistics = OSDextStatistics::create();
		dextUniqueID.reset(parseDextUniqueID(anInfoDict, getIdentifierCString()), OSRetain);
		dextLaunchedCount = 0;
	}

	/* If we created a kext from prelink info,
	 * we must be booting from a prelinked kernel.
	 */
	sPrelinkBoot = true;

	result = (registerIdentifier() == kOSKextInitialized);
finish:
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSSharedPtr<OSKext>
OSKext::withCodelessInfo(OSDictionary * anInfoDict, OSKextInitResult *result)
{
	OSSharedPtr<OSKext> newKext = OSMakeShared<OSKext>();
	if (!newKext) {
		return NULL;
	}

	OSKextInitResult ret = newKext->initWithCodelessInfo(anInfoDict);
	if (result != NULL) {
		*result = ret;
	}
	if (ret != kOSKextInitialized) {
		return NULL;
	}

	return newKext;
}

/*********************************************************************
*********************************************************************/
OSKextInitResult
OSKext::initWithCodelessInfo(OSDictionary * anInfoDict)
{
	OSKextInitResult result          = kOSKextInitFailure;
	OSString  * kextPath             = NULL;        // do not release
	OSBoolean * scratchBool          = NULL;        // do not release

	if (anInfoDict == NULL || !super::init()) {
		goto finish;
	}

	/*
	 * Get the path. Don't look for an arch-specific path property.
	 */
	kextPath = OSDynamicCast(OSString,
	    anInfoDict->getObject(kKextRequestArgumentCodelessInfoBundlePathKey));
	if (!kextPath) {
		OSKextLog(NULL,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Requested codeless kext dictionary does not contain the '%s' key",
		    kKextRequestArgumentCodelessInfoBundlePathKey);
		goto finish;
	}

	uniquePersonalityProperties(anInfoDict);

	if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
		goto finish;
	}

	/*
	 * This path is meant to initialize codeless kexts only. Refuse
	 * anything that looks like it has an executable and/or declares
	 * itself as a kernel component.
	 */
	if (declaresExecutable() || isKernelComponent()) {
		OSKextLog(NULL,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Refusing to register codeless kext that declares an executable/kernel component: %s",
		    getIdentifierCString());
		goto finish;
	}

	if (strcmp(getIdentifierCString(), kIOExcludeListBundleID) == 0) {
		boolean_t updated = updateExcludeList(infoDict.get());
		if (updated) {
			OSKextLog(this,
			    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "KextExcludeList was updated to version: %lld", sExcludeListVersion);
		}
	}

	kc_type = KCKindNone;

	scratchBool = OSDynamicCast(OSBoolean,
	    getPropertyForHostArch(kOSBundleRequireExplicitLoadKey));
	if (scratchBool == kOSBooleanTrue) {
		flags.requireExplicitLoad = 1;
	}

	/* Also get the executable's bundle-relative path if present.
	 * Don't look for an arch-specific path property.
	 */
	userExecutableRelPath.reset(OSDynamicCast(OSString,
	    anInfoDict->getObject(kCFBundleDriverKitExecutableKey)), OSRetain);

	/* remove unnecessary paths from the info dict */
	anInfoDict->removeObject(kKextRequestArgumentCodelessInfoBundlePathKey);

	if (isDriverKit()) {
		dextStatistics = OSDextStatistics::create();
		dextUniqueID.reset(parseDextUniqueID(anInfoDict, getIdentifierCString()), OSRetain);
		dextLaunchedCount = 0;
	}

	result = registerIdentifier();

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::setAllVMAttributes(void)
{
	OSSharedPtr<OSCollectionIterator> kextIterator;
	const OSSymbol * thisID                 = NULL;        // do not release

	IORecursiveLockLock(sKextLock);

	kextIterator = OSCollectionIterator::withCollection(sKextsByID.get());
	if (!kextIterator) {
		goto finish;
	}

	while ((thisID = OSDynamicCast(OSSymbol, kextIterator->getNextObject()))) {
		OSKext *    thisKext;        // do not release

		thisKext = OSDynamicCast(OSKext, sKextsByID->getObject(thisID));
		if (!thisKext || thisKext->isInterface() || !thisKext->declaresExecutable()) {
			continue;
		}

		if (!thisKext->flags.resetSegmentsFromVnode) {
			/*
			 * set VM protections now, wire pages for the old style Aux KC now,
			 * wire pages for the rest of the KC types at load time.
			 */
			thisKext->setVMAttributes(true, (thisKext->kc_type == KCKindAuxiliary) ? true : false);
		}
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::withBooterData(
	OSString * deviceTreeName,
	OSData   * booterData)
{
	OSSharedPtr<OSKext> newKext(OSMakeShared<OSKext>());

	if (newKext && !newKext->initWithBooterData(deviceTreeName, booterData)) {
		return NULL;
	}

	return newKext;
}

/*********************************************************************
*********************************************************************/
typedef struct _BooterKextFileInfo {
	uint32_t  infoDictPhysAddr;
	uint32_t  infoDictLength;
	uint32_t  executablePhysAddr;
	uint32_t  executableLength;
	uint32_t  bundlePathPhysAddr;
	uint32_t  bundlePathLength;
} _BooterKextFileInfo;

bool
OSKext::initWithBooterData(
	OSString * deviceTreeName,
	OSData   * booterData)
{
	bool                  result         = false;
	_BooterKextFileInfo * kextFileInfo   = NULL;        // do not free
	char                * infoDictAddr   = NULL;        // do not free
	void                * executableAddr = NULL;        // do not free
	char                * bundlePathAddr = NULL;        // do not free

	OSDictionary        * theInfoDict    = NULL;        // do not release
	OSSharedPtr<OSObject> parsedXML;
	OSSharedPtr<OSString> kextPath;

	OSSharedPtr<OSString> errorString;
	OSSharedPtr<OSData>   executable;

	if (!super::init()) {
		goto finish;
	}

	kextFileInfo = (_BooterKextFileInfo *)booterData->getBytesNoCopy();
	if (!kextFileInfo) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "No booter-provided data for kext device tree entry %s.",
		    deviceTreeName->getCStringNoCopy());
		goto finish;
	}

	/* The info plist must exist or we can't read the kext.
	 */
	if (!kextFileInfo->infoDictPhysAddr || !kextFileInfo->infoDictLength) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "No kext info dictionary for booter device tree entry %s.",
		    deviceTreeName->getCStringNoCopy());
		goto finish;
	}

	infoDictAddr = (char *)ml_static_ptovirt(kextFileInfo->infoDictPhysAddr);
	if (!infoDictAddr) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "Can't translate physical address 0x%x of kext info dictionary "
		    "for device tree entry %s.",
		    (int)kextFileInfo->infoDictPhysAddr,
		    deviceTreeName->getCStringNoCopy());
		goto finish;
	}

	parsedXML = OSUnserializeXML(infoDictAddr, errorString);
	if (parsedXML) {
		theInfoDict = OSDynamicCast(OSDictionary, parsedXML.get());
	}
	if (!theInfoDict) {
		const char * errorCString = "(unknown error)";

		if (errorString && errorString->getCStringNoCopy()) {
			errorCString = errorString->getCStringNoCopy();
		} else if (parsedXML) {
			errorCString = "not a dictionary";
		}
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "Error unserializing info dictionary for device tree entry %s: %s.",
		    deviceTreeName->getCStringNoCopy(), errorCString);
		goto finish;
	}

	/* A bundle path is not mandatory.
	 */
	if (kextFileInfo->bundlePathPhysAddr && kextFileInfo->bundlePathLength) {
		bundlePathAddr = (char *)ml_static_ptovirt(kextFileInfo->bundlePathPhysAddr);
		if (!bundlePathAddr) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag,
			    "Can't translate physical address 0x%x of kext bundle path "
			    "for device tree entry %s.",
			    (int)kextFileInfo->bundlePathPhysAddr,
			    deviceTreeName->getCStringNoCopy());
			goto finish;
		}
		bundlePathAddr[kextFileInfo->bundlePathLength - 1] = '\0';         // just in case!

		kextPath = OSString::withCString(bundlePathAddr);
		if (!kextPath) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag,
			    "Failed to create wrapper for device tree entry %s kext path %s.",
			    deviceTreeName->getCStringNoCopy(), bundlePathAddr);
			goto finish;
		}
	}

	if (!setInfoDictionaryAndPath(theInfoDict, kextPath.get())) {
		goto finish;
	}

	/* An executable is not mandatory.
	 */
	if (kextFileInfo->executablePhysAddr && kextFileInfo->executableLength) {
		executableAddr = (void *)ml_static_ptovirt(kextFileInfo->executablePhysAddr);
		if (!executableAddr) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag,
			    "Can't translate physical address 0x%x of kext executable "
			    "for device tree entry %s.",
			    (int)kextFileInfo->executablePhysAddr,
			    deviceTreeName->getCStringNoCopy());
			goto finish;
		}

		executable = OSData::withBytesNoCopy(executableAddr,
		    kextFileInfo->executableLength);
		if (!executable) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag,
			    "Failed to create executable wrapper for device tree entry %s.",
			    deviceTreeName->getCStringNoCopy());
			goto finish;
		}

		/* A kext with an executable needs to retain the whole booterData
		 * object to keep the executable in memory.
		 */
		if (!setExecutable(executable.get(), booterData)) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag,
			    "Failed to set kext executable for device tree entry %s.",
			    deviceTreeName->getCStringNoCopy());
			goto finish;
		}
	}

	if (isDriverKit()) {
		dextStatistics = OSDextStatistics::create();
		dextUniqueID.reset(parseDextUniqueID(theInfoDict, getIdentifierCString()), OSRetain);
		dextLaunchedCount = 0;
	}

	result = (registerIdentifier() == kOSKextInitialized);

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
OSKextInitResult
OSKext::registerIdentifier(void)
{
	OSKextInitResult  result                  = kOSKextInitFailure;
	OSKext        * existingKext              = NULL;        // do not release
	bool            existingIsLoaded          = false;
	bool            existingIsPrelinked       = false;
	bool            existingIsCodeless        = false;
	bool            existingIsDext            = false;
	OSKextVersion   newVersion                = -1;
	OSKextVersion   existingVersion           = -1;
	char            newVersionCString[kOSKextVersionMaxLength];
	char            existingVersionCString[kOSKextVersionMaxLength];
	OSSharedPtr<OSData> newUUID;
	OSSharedPtr<OSData> existingUUID;
	const char *newDextUniqueIDCString = NULL;
	const char *existingDextUniqueIDCString = NULL;
	unsigned int newDextUniqueIDCStringSize = 0;
	unsigned int existingDextUniqueIDCStringSize = 0;

	IORecursiveLockLock(sKextLock);

	/* Get the new kext's version for checks & log messages.
	 */
	newVersion = getVersion();
	OSKextVersionGetString(newVersion, newVersionCString,
	    kOSKextVersionMaxLength);

	/* If we don't have an existing kext with this identifier,
	 * just record the new kext and we're done!
	 */
	existingKext = OSDynamicCast(OSKext, sKextsByID->getObject(bundleID.get()));
	if (!existingKext) {
		sKextsByID->setObject(bundleID.get(), this);
		result = kOSKextInitialized;
		goto finish;
	}

	/* Get the existing kext's version for checks & log messages.
	 */
	existingVersion = existingKext->getVersion();
	OSKextVersionGetString(existingVersion,
	    existingVersionCString, kOSKextVersionMaxLength);

	existingIsLoaded = existingKext->isLoaded();
	existingIsPrelinked = existingKext->isPrelinked();
	existingIsDext = existingKext->isDriverKit();
	existingIsCodeless = !existingKext->declaresExecutable() && !existingIsDext;

	/*
	 * Check if we are trying to upgrade a dext
	 * with another dext.
	 */
	if (isDriverKit() && existingIsDext) {
		OSData *newDextUID = getDextUniqueID();
		if (!newDextUID) {
			OSKextLog(this,
			    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "New dext %s, v%s requested does not have a unique dext identifier\n",
			    getIdentifierCString(), newVersionCString);
			goto finish;
		}
		newDextUniqueIDCString = getDextUniqueIDCString(newDextUID, &newDextUniqueIDCStringSize);
		assert(newDextUniqueIDCString != NULL);

		OSData *existingDextUID = existingKext->getDextUniqueID();
		if (!existingDextUID) {
			OSKextLog(this,
			    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "Found a dext %s, v%s: with no unique dext identifier\n",
			    existingKext->getIdentifierCString(), existingVersionCString);
			goto finish;
		}
		existingDextUniqueIDCString = getDextUniqueIDCString(existingDextUID, &existingDextUniqueIDCStringSize);
		assert(existingDextUniqueIDCString != NULL);

		/*
		 * We might get multiple requests to save the same dext.
		 * Check if we already have saved it or if this is an upgrade
		 * for a dext with the same BundleID.
		 * Dexts are uniquely identified by DextUniqueID, if a new DextUniqueID
		 * is requested for a BundleID we are going to upgrade to the newest
		 * received irrespective from the dext version.
		 */
		if (newDextUID->isEqualTo(existingDextUID) && existingKext->flags.dextToReplace == 0) {
			OSKextLog(this,
			    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "Refusing new dext %s, v%s:"
			    "a dext v %s with the same unique dext identifier (%s) already exists\n",
			    getIdentifierCString(), newVersionCString,
			    existingVersionCString, newDextUniqueIDCString);
			result = kOSKextAlreadyExist;
			goto finish;
		}

		bool upgraded = upgradeDext(existingKext, this);
		if (upgraded) {
			/* If the dext was upgraded existingKext might have been deallocated */
			existingKext = NULL;
			OSKextLog(this,
			    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "Dext %s, v%s , unique dext identifier %s "
			    "Upgraded to v%s, unique dext identifier %s \n",
			    getIdentifierCString(), existingVersionCString, existingDextUniqueIDCString,
			    newVersionCString, newDextUniqueIDCString);
			result = kOSKextInitialized;
		} else {
			OSKextLog(this,
			    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
			    "Upgrade delayed for %s v%s, unique dext identifier %s "
			    "with v%s, unique dext identifier %s.\n",
			    getIdentifierCString(), existingVersionCString, existingDextUniqueIDCString,
			    newVersionCString, newDextUniqueIDCString);
			result = kOSKextAlreadyExist;
		}

		goto finish;
	}

	/* If we have a non-codeless kext with this identifier that's already
	 * loaded/prelinked, we can't use the new one, but let's be really
	 * thorough and check how the two are related for a precise diagnostic
	 * log message.
	 *
	 * This check is valid for kexts that declare an executable and for
	 * dexts, but not for codeless kexts - we can just replace those.
	 */
	if ((!existingIsCodeless || existingIsDext) &&
	    (existingIsLoaded || existingIsPrelinked)) {
		bool sameVersion = (newVersion == existingVersion);
		bool sameExecutable = true;         // assume true unless we have UUIDs

		/* Only get the UUID if the existing kext is loaded. Doing so
		 * might have to uncompress an mkext executable and we shouldn't
		 * take that hit when neither kext is loaded.
		 *
		 * Note: there is no decompression that happens when all kexts
		 * are loaded from kext collecitons.
		 */
		newUUID = copyUUID();
		existingUUID = existingKext->copyUUID();

		if (existingIsDext && !isDriverKit()) {
			OSKextLog(this,
			    kOSKextLogWarningLevel |
			    kOSKextLogKextBookkeepingFlag,
			    "Notice - new kext %s, v%s matches a %s dext"
			    "with the same bundle ID, v%s.",
			    getIdentifierCString(), newVersionCString,
			    (existingIsLoaded ? "loaded" : "prelinked"),
			    existingVersionCString);
			goto finish;
		}

		/* I'm entirely too paranoid about checking equivalence of executables,
		 * but I remember nasty problems with it in the past.
		 *
		 * - If we have UUIDs for both kexts, compare them.
		 * - If only one kext has a UUID, they're definitely different.
		 */
		if (newUUID && existingUUID) {
			sameExecutable = newUUID->isEqualTo(existingUUID.get());
		} else if (newUUID || existingUUID) {
			sameExecutable = false;
		}

		if (!newUUID && !existingUUID) {
			/* If there are no UUIDs, we can't really tell that the executables
			 * are *different* without a lot of work; the loaded kext's
			 * unrelocated executable is no longer around (and we never had it
			 * in-kernel for a prelinked kext). We certainly don't want to do
			 * a whole fake link for the new kext just to compare, either.
			 */
			OSKextLog(this,
			    kOSKextLogWarningLevel |
			    kOSKextLogKextBookkeepingFlag,
			    "Notice - new kext %s, v%s matches %s kext "
			    "but can't determine if executables are the same (no UUIDs).",
			    getIdentifierCString(),
			    newVersionCString,
			    (existingIsLoaded ? "loaded" : "prelinked"));
		}

		if (sameVersion && sameExecutable) {
			OSKextLog(this,
			    (existingIsLoaded ? kOSKextLogWarningLevel : kOSKextLogStepLevel) |
			    kOSKextLogKextBookkeepingFlag,
			    "Refusing new kext %s, v%s: a %s copy is already present "
			    "(same version and executable).",
			    getIdentifierCString(), newVersionCString,
			    (existingIsLoaded ? "loaded" : "prelinked"));
		} else {
			if (!sameVersion) {
				/* This condition is significant so log it under warnings.
				 */
				OSKextLog(this,
				    kOSKextLogWarningLevel |
				    kOSKextLogKextBookkeepingFlag,
				    "Refusing new kext %s, v%s: already have %s v%s.",
				    getIdentifierCString(),
				    newVersionCString,
				    (existingIsLoaded ? "loaded" : "prelinked"),
				    existingVersionCString);
			} else {
				/* This condition is significant so log it under warnings.
				 */
				OSKextLog(this,
				    kOSKextLogWarningLevel | kOSKextLogKextBookkeepingFlag,
				    "Refusing new kext %s, v%s: a %s copy with a different "
				    "executable UUID is already present.",
				    getIdentifierCString(), newVersionCString,
				    (existingIsLoaded ? "loaded" : "prelinked"));
			}
		}
		goto finish;
	} /* if ((!existingIsCodeless || existingIsDext) && (existingIsLoaded || existingIsPrelinked)) */

	/* Refuse to allow an existing loaded codeless kext be replaced by a
	 * normal kext with the same bundle ID.
	 */
	if (existingIsCodeless && declaresExecutable()) {
		OSKextLog(this,
		    kOSKextLogWarningLevel | kOSKextLogKextBookkeepingFlag,
		    "Refusing new kext %s, v%s: a codeless copy is already %s",
		    getIdentifierCString(), newVersionCString,
		    (existingIsLoaded ? "loaded" : "prelinked"));
		goto finish;
	}

	/* Dexts packaged in the BootKC will be protected against replacement
	 * by non-dexts by the logic above which checks if they are prelinked.
	 * Dexts which are prelinked into the System KC will be registered
	 * before any other kexts in the AuxKC are registered, and we never
	 * put dexts in the AuxKC. Therefore, there is no need to check if an
	 * existing object is a dext and is being replaced by a non-dext.
	 * The scenario cannot happen by construction.
	 *
	 * See: OSKext::loadFileSetKexts()
	 */


	/* We have two nonloaded/nonprelinked kexts, so our decision depends on whether
	 * user loads are happening or if we're still in early boot. User agents are
	 * supposed to resolve dependencies topside and include only the exact
	 * kexts needed; so we always accept the new kext (in fact we should never
	 * see an older unloaded copy hanging around).
	 */
	if (sUserLoadsActive) {
		sKextsByID->setObject(bundleID.get(), this);
		result = kOSKextInitialized;

		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Dropping old copy of kext %s (v%s) for newly-added (v%s).",
		    getIdentifierCString(),
		    existingVersionCString,
		    newVersionCString);

		goto finish;
	}

	/* During early boot, the kext with the highest version always wins out.
	 * Prelinked kernels will never hit this, but mkexts and booter-read
	 * kexts might have duplicates.
	 */
	if (newVersion > existingVersion) {
		sKextsByID->setObject(bundleID.get(), this);
		result = kOSKextInitialized;

		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Dropping lower version (v%s) of registered kext %s for higher (v%s).",
		    existingVersionCString,
		    getIdentifierCString(),
		    newVersionCString);
	} else {
		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Kext %s is already registered with a higher/same version (v%s); "
		    "dropping newly-added (v%s).",
		    getIdentifierCString(),
		    existingVersionCString,
		    newVersionCString);
	}

	/* result has been set appropriately by now. */

finish:

	IORecursiveLockUnlock(sKextLock);

	if (newDextUniqueIDCString != NULL) {
		kfree_data(newDextUniqueIDCString, newDextUniqueIDCStringSize);
	}
	if (existingDextUniqueIDCString != NULL) {
		kfree_data(existingDextUniqueIDCString, existingDextUniqueIDCStringSize);
	}

	if (result == kOSKextInitialized) {
		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Kext %s, v%s registered and available for loading.",
		    getIdentifierCString(), newVersionCString);
	}

	return result;
}

/*********************************************************************
 * Does the bare minimum validation to look up a kext.
 * All other validation is done on the spot as needed.
 **********************************************************************/
bool
OSKext::setInfoDictionaryAndPath(
	OSDictionary * aDictionary,
	OSString     * aPath)
{
	bool           result                   = false;
	OSString     * bundleIDString           = NULL;        // do not release
	OSString     * versionString            = NULL;        // do not release
	OSString     * compatibleVersionString  = NULL;        // do not release
	const char   * versionCString           = NULL;        // do not free
	const char   * compatibleVersionCString = NULL;        // do not free
	OSBoolean    * scratchBool              = NULL;        // do not release
	OSDictionary * scratchDict              = NULL;        // do not release

	if (infoDict) {
		panic("Attempt to set info dictionary on a kext "
		    "that already has one (%s).",
		    getIdentifierCString());
	}

	if (!aDictionary || !OSDynamicCast(OSDictionary, aDictionary)) {
		goto finish;
	}

	infoDict.reset(aDictionary, OSRetain);

	/* Check right away if the info dictionary has any log flags.
	 */
	scratchBool = OSDynamicCast(OSBoolean,
	    getPropertyForHostArch(kOSBundleEnableKextLoggingKey));
	if (scratchBool == kOSBooleanTrue) {
		flags.loggingEnabled = 1;
	}

	/* The very next thing to get is the bundle identifier. Unlike
	 * in user space, a kext with no bundle identifier gets axed
	 * immediately.
	 */
	bundleIDString = OSDynamicCast(OSString,
	    getPropertyForHostArch(kCFBundleIdentifierKey));
	if (!bundleIDString) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag,
		    "CFBundleIdentifier missing/invalid type in kext %s.",
		    aPath ? aPath->getCStringNoCopy() : "(unknown)");
		goto finish;
	}
	bundleID = OSSymbol::withString(bundleIDString);
	if (!bundleID) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag,
		    "Can't copy bundle identifier as symbol for kext %s.",
		    bundleIDString->getCStringNoCopy());
		goto finish;
	}

	/* Save the path if we got one (it should always be available but it's
	 * just something nice to have for bookkeeping).
	 */
	if (aPath) {
		path.reset(aPath, OSRetain);
	}

	/*****
	 * Minimal validation to initialize. We'll do other validation on the spot.
	 */
	if (bundleID->getLength() >= KMOD_MAX_NAME) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag,
		    "Kext %s error - CFBundleIdentifier over max length %d.",
		    getIdentifierCString(), KMOD_MAX_NAME - 1);
		goto finish;
	}

	version = compatibleVersion = -1;

	versionString = OSDynamicCast(OSString,
	    getPropertyForHostArch(kCFBundleVersionKey));
	if (!versionString) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag,
		    "Kext %s error - CFBundleVersion missing/invalid type.",
		    getIdentifierCString());
		goto finish;
	}
	versionCString = versionString->getCStringNoCopy();
	version = OSKextParseVersionString(versionCString);
	if (version < 0) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag,
		    "Kext %s error - CFBundleVersion bad value '%s'.",
		    getIdentifierCString(), versionCString);
		goto finish;
	}

	compatibleVersion = -1;         // set to illegal value for kexts that don't have

	compatibleVersionString = OSDynamicCast(OSString,
	    getPropertyForHostArch(kOSBundleCompatibleVersionKey));
	if (compatibleVersionString) {
		compatibleVersionCString = compatibleVersionString->getCStringNoCopy();
		compatibleVersion = OSKextParseVersionString(compatibleVersionCString);
		if (compatibleVersion < 0) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogValidationFlag,
			    "Kext %s error - OSBundleCompatibleVersion bad value '%s'.",
			    getIdentifierCString(), compatibleVersionCString);
			goto finish;
		}

		if (compatibleVersion > version) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogValidationFlag,
			    "Kext %s error - %s %s > %s %s (must be <=).",
			    getIdentifierCString(),
			    kOSBundleCompatibleVersionKey, compatibleVersionCString,
			    kCFBundleVersionKey, versionCString);
			goto finish;
		}
	}

	/* Check to see if this kext is in exclude list */
	if (isInExcludeList()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
		    "Kext %s is in exclude list, not loadable",
		    getIdentifierCString());
		goto finish;
	}

	/* Set flags for later use if the infoDict gets flushed. We only
	 * check for true values, not false ones(!)
	 */
	scratchBool = OSDynamicCast(OSBoolean,
	    getPropertyForHostArch(kOSBundleIsInterfaceKey));
	if (scratchBool == kOSBooleanTrue) {
		flags.interface = 1;
	}

	scratchBool = OSDynamicCast(OSBoolean,
	    getPropertyForHostArch(kOSKernelResourceKey));
	if (scratchBool == kOSBooleanTrue) {
		flags.kernelComponent = 1;
		flags.interface = 1;         // xxx - hm. the kernel itself isn't an interface...
		flags.started = 1;

		/* A kernel component has one implicit dependency on the kernel.
		 */
		flags.hasAllDependencies = 1;
	}

	/* Make sure common string values in personalities are uniqued to OSSymbols.
	 */
	scratchDict = OSDynamicCast(OSDictionary,
	    getPropertyForHostArch(kIOKitPersonalitiesKey));
	if (scratchDict) {
		uniquePersonalityProperties(scratchDict);
	}

	result = true;

finish:

	return result;
}

/*********************************************************************
* Not used for prelinked kernel boot as there is no unrelocated
* executable.
*********************************************************************/
bool
OSKext::setExecutable(
	OSData * anExecutable,
	OSData * externalData,
	bool     externalDataIsMkext)
{
	bool         result        = false;
	const char * executableKey = NULL;         // do not free

	if (!anExecutable) {
		infoDict->removeObject(_kOSKextExecutableKey);
		infoDict->removeObject(_kOSKextMkextExecutableReferenceKey);
		infoDict->removeObject(_kOSKextExecutableExternalDataKey);
		result = true;
		goto finish;
	}

	if (infoDict->getObject(_kOSKextExecutableKey) ||
	    infoDict->getObject(_kOSKextMkextExecutableReferenceKey)) {
		panic("Attempt to set an executable on a kext "
		    "that already has one (%s).",
		    getIdentifierCString());
		goto finish;
	}

	if (externalDataIsMkext) {
		executableKey = _kOSKextMkextExecutableReferenceKey;
	} else {
		executableKey = _kOSKextExecutableKey;
	}

	if (anExecutable) {
		infoDict->setObject(executableKey, anExecutable);
		if (externalData) {
			infoDict->setObject(_kOSKextExecutableExternalDataKey, externalData);
		}
	}

	result = true;

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
static void
uniqueStringPlistProperty(OSDictionary * dict, const char * key)
{
	OSObject       * value       = NULL;        // do not release
	OSString       * stringValue = NULL;        // do not release
	OSSharedPtr<const OSSymbol> symbolValue;

	value = dict->getObject(key);
	if (!value) {
		goto finish;
	}
	if (OSDynamicCast(OSSymbol, value)) {
		/* this is already an OSSymbol: we're good */
		goto finish;
	}

	stringValue = OSDynamicCast(OSString, value);
	if (!stringValue) {
		goto finish;
	}

	symbolValue = OSSymbol::withString(stringValue);
	if (!symbolValue) {
		goto finish;
	}

	dict->setObject(key, symbolValue.get());

finish:
	return;
}

/*********************************************************************
*********************************************************************/
static void
uniqueStringPlistProperty(OSDictionary * dict, const OSString * key)
{
	OSObject       * value       = NULL;        // do not release
	OSString       * stringValue = NULL;        // do not release
	OSSharedPtr<const OSSymbol> symbolValue;

	value = dict->getObject(key);
	if (!value) {
		goto finish;
	}
	if (OSDynamicCast(OSSymbol, value)) {
		/* this is already an OSSymbol: we're good */
		goto finish;
	}

	stringValue = OSDynamicCast(OSString, value);
	if (!stringValue) {
		goto finish;
	}

	symbolValue = OSSymbol::withString(stringValue);
	if (!symbolValue) {
		goto finish;
	}

	dict->setObject(key, symbolValue.get());

finish:
	return;
}

void
OSKext::uniquePersonalityProperties(OSDictionary * personalityDict)
{
	OSKext::uniquePersonalityProperties(personalityDict, true);
}

/*********************************************************************
* Replace common personality property values with uniqued instances
* to save on wired memory.
*********************************************************************/
/* static */
void
OSKext::uniquePersonalityProperties(OSDictionary * personalityDict, bool defaultAddKernelBundleIdentifier)
{
	/* Properties every personality has.
	 */
	uniqueStringPlistProperty(personalityDict, kCFBundleIdentifierKey);
	uniqueStringPlistProperty(personalityDict, kIOProviderClassKey);
	uniqueStringPlistProperty(personalityDict, gIOClassKey.get());
	if (personalityDict->getObject(kCFBundleIdentifierKernelKey)) {
		uniqueStringPlistProperty(personalityDict, kCFBundleIdentifierKernelKey);
	} else if (defaultAddKernelBundleIdentifier) {
		personalityDict->setObject(kCFBundleIdentifierKernelKey, personalityDict->getObject(kCFBundleIdentifierKey));
	}

	/* Other commonly used properties.
	 */
	uniqueStringPlistProperty(personalityDict, gIOMatchCategoryKey);
	uniqueStringPlistProperty(personalityDict, gIOResourceMatchKey);
	uniqueStringPlistProperty(personalityDict, gIOUserClientClassKey);

	uniqueStringPlistProperty(personalityDict, "HIDDefaultBehavior");
	uniqueStringPlistProperty(personalityDict, "HIDPointerAccelerationType");
	uniqueStringPlistProperty(personalityDict, "HIDRemoteControlType");
	uniqueStringPlistProperty(personalityDict, "HIDScrollAccelerationType");
	uniqueStringPlistProperty(personalityDict, "IOPersonalityPublisher");
	uniqueStringPlistProperty(personalityDict, "Physical Interconnect");
	uniqueStringPlistProperty(personalityDict, "Physical Interconnect Location");
	uniqueStringPlistProperty(personalityDict, "Vendor");
	uniqueStringPlistProperty(personalityDict, "Vendor Identification");
	uniqueStringPlistProperty(personalityDict, "Vendor Name");
	uniqueStringPlistProperty(personalityDict, "bConfigurationValue");
	uniqueStringPlistProperty(personalityDict, "bInterfaceNumber");
	uniqueStringPlistProperty(personalityDict, "idProduct");

	return;
}

/*********************************************************************
*********************************************************************/
void
OSKext::free(void)
{
	if (isLoaded()) {
		panic("Attempt to free loaded kext %s.", getIdentifierCString());
	}

	if (isDriverKit()) {
		if (dextLaunchedCount > 0) {
			panic("Freeing dext %s but dextLaunchedCount is %d\n", getIdentifierCString(), dextLaunchedCount);
		}
	}

	infoDict.reset();
	bundleID.reset();
	path.reset();
	executableRelPath.reset();
	userExecutableRelPath.reset();
	dependencies.reset();
	linkedExecutable.reset();
	metaClasses.reset();
	interfaceUUID.reset();
	driverKitUUID.reset();
	dextStatistics.reset();
	dextUniqueID.reset();

	if (isInterface() && kmod_info) {
		kfree_type(kmod_info_t, kmod_info);
	}

	super::free();
	return;
}

#if PRAGMA_MARK
#pragma mark Mkext files
#endif

#if CONFIG_KXLD
/*
 * mkext archives are really only relevant on kxld-enabled kernels.
 * Without a dynamic kernel linker, we don't need to support any mkexts.
 */

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::readMkextArchive(OSData * mkextData,
    uint32_t * checksumPtr)
{
	OSReturn       result       = kOSKextReturnBadData;
	uint32_t       mkextLength  = 0;
	mkext_header * mkextHeader  = NULL;        // do not free
	uint32_t       mkextVersion = 0;

	/* Note default return of kOSKextReturnBadData above.
	 */
	mkextLength = mkextData->getLength();
	if (mkextLength < sizeof(mkext_basic_header)) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive too small to be valid.");
		goto finish;
	}

	mkextHeader = (mkext_header *)mkextData->getBytesNoCopy();

	if (MKEXT_GET_MAGIC(mkextHeader) != MKEXT_MAGIC ||
	    MKEXT_GET_SIGNATURE(mkextHeader) != MKEXT_SIGN) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive has invalid magic or signature.");
		goto finish;
	}

	if (MKEXT_GET_LENGTH(mkextHeader) != mkextLength) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive recorded length doesn't match actual file length.");
		goto finish;
	}

	mkextVersion = MKEXT_GET_VERSION(mkextHeader);

	if (mkextVersion == MKEXT_VERS_2) {
		result = OSKext::readMkext2Archive(mkextData, NULL, checksumPtr);
	} else {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive of unsupported mkext version 0x%x.", mkextVersion);
		result = kOSKextReturnUnsupported;
	}

finish:
	return result;
}

/*********************************************************************
* Assumes magic, signature, version, length have been checked.
* xxx - need to add further bounds checking for each file entry
*
* Should keep track of all kexts created so far, and if we hit a
* fatal error halfway through, remove those kexts. If we've dropped
* an older version that had already been read, whoops! Might want to
* add a level of buffering?
*********************************************************************/
/* static */
OSReturn
OSKext::readMkext2Archive(
	OSData        * mkextData,
	OSDictionary ** mkextPlistOut,
	uint32_t      * checksumPtr)
{
	OSReturn        result                     = kOSReturnError;
	uint32_t        mkextLength;
	mkext2_header * mkextHeader                = NULL;        // do not free
	void          * mkextEnd                   = NULL;        // do not free
	uint32_t        mkextVersion;
	uint8_t       * crc_address                = NULL;
	size_t          crc_buffer_size            = 0;
	uint32_t        checksum;
	uint32_t        mkextPlistOffset;
	uint32_t        mkextPlistCompressedSize;
	char          * mkextPlistEnd              = NULL;        // do not free
	uint32_t        mkextPlistFullSize;
	OSSharedPtr<OSString>     errorString;
	OSSharedPtr<OSData>       mkextPlistUncompressedData;
	const char    * mkextPlistDataBuffer       = NULL;        // do not free
	OSSharedPtr<OSObject>      parsedXML;
	OSDictionary  * mkextPlist                 = NULL;        // do not release
	OSArray       * mkextInfoDictArray         = NULL;        // do not release
	uint32_t        count, i;
	kc_format_t kc_format;

	if (!PE_get_primary_kc_format(&kc_format)) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
		    "Unable to determine primary KC format");
		goto finish;
	}

	mkextLength = mkextData->getLength();
	mkextHeader = (mkext2_header *)mkextData->getBytesNoCopy();
	mkextEnd = (char *)mkextHeader + mkextLength;
	mkextVersion = MKEXT_GET_VERSION(mkextHeader);

	crc_address = (u_int8_t *)&mkextHeader->version;
	crc_buffer_size = (uintptr_t)mkextHeader +
	    MKEXT_GET_LENGTH(mkextHeader) - (uintptr_t)crc_address;
	if (crc_buffer_size > INT32_MAX) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive size is too large (%lu > INT32_MAX).",
		    crc_buffer_size);
		result = kOSKextReturnBadData;
		goto finish;
	}
	checksum = mkext_adler32(crc_address, (int32_t)crc_buffer_size);

	if (MKEXT_GET_CHECKSUM(mkextHeader) != checksum) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive has bad checksum.");
		result = kOSKextReturnBadData;
		goto finish;
	}

	if (checksumPtr) {
		*checksumPtr = checksum;
	}

	/* Check that the CPU type & subtype match that of the running kernel. */
	if (MKEXT_GET_CPUTYPE(mkextHeader) == (UInt32)CPU_TYPE_ANY) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive must have a specific CPU type.");
		result = kOSKextReturnBadData;
		goto finish;
	} else {
		if ((UInt32)_mh_execute_header.cputype !=
		    MKEXT_GET_CPUTYPE(mkextHeader)) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Mkext archive does not match the running kernel's CPU type.");
			result = kOSKextReturnArchNotFound;
			goto finish;
		}
	}

	mkextPlistOffset = MKEXT2_GET_PLIST(mkextHeader);
	mkextPlistCompressedSize = MKEXT2_GET_PLIST_COMPSIZE(mkextHeader);
	mkextPlistEnd = (char *)mkextHeader + mkextPlistOffset +
	    mkextPlistCompressedSize;
	if (mkextPlistEnd > mkextEnd) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive file overrun.");
		result = kOSKextReturnBadData;
	}

	mkextPlistFullSize = MKEXT2_GET_PLIST_FULLSIZE(mkextHeader);
	if (mkextPlistCompressedSize) {
		mkextPlistUncompressedData = sKernelKext->extractMkext2FileData(
			(UInt8 *)mkextHeader + mkextPlistOffset,
			"plist",
			mkextPlistCompressedSize, mkextPlistFullSize);
		if (!mkextPlistUncompressedData) {
			goto finish;
		}
		mkextPlistDataBuffer = (const char *)
		    mkextPlistUncompressedData->getBytesNoCopy();
	} else {
		mkextPlistDataBuffer = (const char *)mkextHeader + mkextPlistOffset;
	}

	/* IOCFSerialize added a nul byte to the end of the string. Very nice of it.
	 */
	parsedXML = OSUnserializeXML(mkextPlistDataBuffer, errorString);
	if (parsedXML) {
		mkextPlist = OSDynamicCast(OSDictionary, parsedXML.get());
	}
	if (!mkextPlist) {
		const char * errorCString = "(unknown error)";

		if (errorString && errorString->getCStringNoCopy()) {
			errorCString = errorString->getCStringNoCopy();
		} else if (parsedXML) {
			errorCString = "not a dictionary";
		}
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Error unserializing mkext plist: %s.", errorCString);
		goto finish;
	}

	mkextInfoDictArray = OSDynamicCast(OSArray,
	    mkextPlist->getObject(kMKEXTInfoDictionariesKey));
	if (!mkextInfoDictArray) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Mkext archive contains no kext info dictionaries.");
		goto finish;
	}

	count = mkextInfoDictArray->getCount();
	for (i = 0; i < count; i++) {
		OSDictionary * infoDict;


		infoDict = OSDynamicCast(OSDictionary,
		    mkextInfoDictArray->getObject(i));

		/* Create the kext for the entry, then release it, because the
		 * kext system keeps them around until explicitly removed.
		 * Any creation/registration failures are already logged for us.
		 */
		if (infoDict) {
			OSSharedPtr<OSKext> newKext = OSKext::withMkext2Info(infoDict, mkextData);

			/* Fail dynamic loading of a kext when booted from MH_FILESET */
			if (kc_format == KCFormatFileset &&
			    newKext &&
			    !(newKext->isPrelinked()) &&
			    newKext->declaresExecutable()) {
				result = kOSReturnError;
				printf("Kext LOG: Dynamic loading of kext denied for kext %s\n",
				    newKext->getIdentifier() ? newKext->getIdentifierCString() : "unknown kext");

				OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
				    "Dynamic loading of kext denied for kext %s\n",
				    newKext->getIdentifier() ? newKext->getIdentifierCString() : "unknown kext");
				goto finish;
			}
		}
	}

	/* If the caller needs the plist, hand them back our copy
	 */
	if (mkextPlistOut) {
		*mkextPlistOut = mkextPlist;
		parsedXML.detach();
	}

	/* Even if we didn't keep any kexts from the mkext, we may have a load
	 * request to process, so we are successful (no errors occurred).
	 */
	result = kOSReturnSuccess;

finish:
	return result;
}

/* static */
OSReturn
OSKext::readMkext2Archive(
	OSData        * mkextData,
	OSSharedPtr<OSDictionary> &mkextPlistOut,
	uint32_t      * checksumPtr)
{
	OSDictionary * mkextPlist = NULL;
	OSReturn ret;

	if (kOSReturnSuccess == (ret = readMkext2Archive(mkextData,
	    &mkextPlist,
	    checksumPtr))) {
		mkextPlistOut.reset(mkextPlist, OSNoRetain);
	}
	return ret;
}

/*********************************************************************
*********************************************************************/
/* static */
OSSharedPtr<OSKext>
OSKext::withMkext2Info(
	OSDictionary * anInfoDict,
	OSData       * mkextData)
{
	OSSharedPtr<OSKext> newKext = OSMakeShared<OSKext>();

	if (newKext && !newKext->initWithMkext2Info(anInfoDict, mkextData)) {
		return NULL;
	}

	return newKext;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::initWithMkext2Info(
	OSDictionary * anInfoDict,
	OSData       * mkextData)
{
	bool                   result              = false;
	OSString             * kextPath            = NULL;        // do not release
	OSNumber             * executableOffsetNum = NULL;        // do not release
	OSSharedPtr<OSData>               executable;

	if (anInfoDict == NULL || !super::init()) {
		goto finish;
	}

	/* Get the path. Don't look for an arch-specific path property.
	 */
	kextPath = OSDynamicCast(OSString,
	    anInfoDict->getObject(kMKEXTBundlePathKey));

	if (!setInfoDictionaryAndPath(anInfoDict, kextPath)) {
		goto finish;
	}

	/* If we have a path to the executable, save it.
	 */
	executableRelPath.reset(OSDynamicCast(OSString,
	    anInfoDict->getObject(kMKEXTExecutableRelativePathKey)), OSRetain);

	/* Don't need the paths to be in the info dictionary any more.
	 */
	anInfoDict->removeObject(kMKEXTBundlePathKey);
	anInfoDict->removeObject(kMKEXTExecutableRelativePathKey);

	executableOffsetNum = OSDynamicCast(OSNumber,
	    infoDict->getObject(kMKEXTExecutableKey));
	if (executableOffsetNum) {
		executable = createMkext2FileEntry(mkextData,
		    executableOffsetNum, "executable");
		infoDict->removeObject(kMKEXTExecutableKey);
		if (!executable) {
			goto finish;
		}
		if (!setExecutable(executable.get(), mkextData, true)) {
			goto finish;
		}
	}

	result = (registerIdentifier() == kOSKextInitialized);
finish:
	return result;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSData>
OSKext::createMkext2FileEntry(
	OSData     * mkextData,
	OSNumber   * offsetNum,
	const char * name)
{
	OSSharedPtr<OSData> result;
	MkextEntryRef   entryRef;
	uint8_t       * mkextBuffer = (uint8_t *)mkextData->getBytesNoCopy();
	uint32_t        entryOffset = offsetNum->unsigned32BitValue();

	result = OSData::withCapacity(sizeof(entryRef));
	if (!result) {
		goto finish;
	}

	entryRef.mkext = (mkext_basic_header *)mkextBuffer;
	entryRef.fileinfo = mkextBuffer + entryOffset;
	if (!result->appendValue(entryRef)) {
		result.reset();
		goto finish;
	}

finish:
	if (!result) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Can't create wrapper for mkext file entry '%s' of kext %s.",
		    name, getIdentifierCString());
	}
	return result;
}

/*********************************************************************
*********************************************************************/
extern "C" {
static void * z_alloc(void *, u_int items, u_int size);
static void   z_free(void *, void *ptr);

typedef struct z_mem {
	uint32_t alloc_size;
	uint8_t  data[0];
} z_mem;

/*
 * Space allocation and freeing routines for use by zlib routines.
 */
void *
z_alloc(void * notused __unused, u_int num_items, u_int size)
{
	void     * result = NULL;
	z_mem    * zmem = NULL;

	uint64_t   total = ((uint64_t)num_items) * ((uint64_t)size);
	//Check for overflow due to multiplication
	if (total > UINT32_MAX) {
		panic("z_alloc(%p, %x, %x): overflow caused by %x * %x",
		    notused, num_items, size, num_items, size);
	}

	uint64_t   allocSize64 =  total + ((uint64_t)sizeof(zmem));
	//Check for overflow due to addition
	if (allocSize64 > UINT32_MAX) {
		panic("z_alloc(%p, %x, %x): overflow caused by %x + %lx",
		    notused, num_items, size, (uint32_t)total, sizeof(zmem));
	}
	uint32_t allocSize = (uint32_t)allocSize64;

	zmem = (z_mem *)kalloc_data_tag(allocSize, Z_WAITOK,
	    VM_KERN_MEMORY_OSKEXT);
	if (!zmem) {
		goto finish;
	}
	zmem->alloc_size = allocSize;
	result = (void *)&(zmem->data);
finish:
	return result;
}

void
z_free(void * notused __unused, void * ptr)
{
	uint32_t * skipper = (uint32_t *)ptr - 1;
	z_mem    * zmem = (z_mem *)skipper;
	kfree_data(zmem, zmem->alloc_size);
}
};

OSSharedPtr<OSData>
OSKext::extractMkext2FileData(
	UInt8      * data,
	const char * name,
	uint32_t     compressedSize,
	uint32_t     fullSize)
{
	OSSharedPtr<OSData>      result;
	OSSharedPtr<OSData>      uncompressedData;        // release on error

	uint8_t     * uncompressedDataBuffer = NULL;        // do not free
	unsigned long uncompressedSize;
	z_stream      zstream;
	bool          zstream_inited = false;
	int           zlib_result;

	/* If the file isn't compressed, we want to make a copy
	 * so that we don't have the tie to the larger mkext file buffer any more.
	 */
	if (!compressedSize) {
		uncompressedData = OSData::withBytes(data, fullSize);
		// xxx - no check for failure?
		result = uncompressedData;
		goto finish;
	}

	if (KERN_SUCCESS != kmem_alloc(kernel_map,
	    (vm_offset_t*)&uncompressedDataBuffer, fullSize,
	    KMA_DATA_SHARED, VM_KERN_MEMORY_OSKEXT)) {
		/* How's this for cheesy? The kernel is only asked to extract
		 * kext plists so we tailor the log messages.
		 */
		if (isKernel()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Allocation failure extracting %s from mkext.", name);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Allocation failure extracting %s from mkext for kext %s.",
			    name, getIdentifierCString());
		}

		goto finish;
	}
	uncompressedData = OSData::withBytesNoCopy(uncompressedDataBuffer, fullSize);
	if (!uncompressedData) {
		if (isKernel()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Allocation failure extracting %s from mkext.", name);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Allocation failure extracting %s from mkext for kext %s.",
			    name, getIdentifierCString());
		}
		goto finish;
	}
	uncompressedData->setDeallocFunction(&osdata_kmem_free);

	if (isKernel()) {
		OSKextLog(this,
		    kOSKextLogDetailLevel |
		    kOSKextLogArchiveFlag,
		    "Kernel extracted %s from mkext - compressed size %d, uncompressed size %d.",
		    name, compressedSize, fullSize);
	} else {
		OSKextLog(this,
		    kOSKextLogDetailLevel |
		    kOSKextLogArchiveFlag,
		    "Kext %s extracted %s from mkext - compressed size %d, uncompressed size %d.",
		    getIdentifierCString(), name, compressedSize, fullSize);
	}

	bzero(&zstream, sizeof(zstream));
	zstream.next_in   = (UInt8 *)data;
	zstream.avail_in  = compressedSize;

	zstream.next_out  = uncompressedDataBuffer;
	zstream.avail_out = fullSize;

	zstream.zalloc    = z_alloc;
	zstream.zfree     = z_free;

	zlib_result = inflateInit(&zstream);
	if (Z_OK != zlib_result) {
		if (isKernel()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Mkext error; zlib inflateInit failed (%d) for %s.",
			    zlib_result, name);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Kext %s - mkext error; zlib inflateInit failed (%d) for %s .",
			    getIdentifierCString(), zlib_result, name);
		}
		goto finish;
	} else {
		zstream_inited = true;
	}

	zlib_result = inflate(&zstream, Z_FINISH);

	if (zlib_result == Z_STREAM_END || zlib_result == Z_OK) {
		uncompressedSize = zstream.total_out;
	} else {
		if (isKernel()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Mkext error; zlib inflate failed (%d) for %s.",
			    zlib_result, name);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Kext %s - mkext error; zlib inflate failed (%d) for %s .",
			    getIdentifierCString(), zlib_result, name);
		}
		if (zstream.msg) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "zlib error: %s.", zstream.msg);
		}
		goto finish;
	}

	if (uncompressedSize != fullSize) {
		if (isKernel()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Mkext error; zlib inflate discrepancy for %s, "
			    "uncompressed size != original size.", name);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Kext %s - mkext error; zlib inflate discrepancy for %s, "
			    "uncompressed size != original size.",
			    getIdentifierCString(), name);
		}
		goto finish;
	}

	result = os::move(uncompressedData);

finish:
	/* Don't bother checking return, nothing we can do on fail.
	 */
	if (zstream_inited) {
		inflateEnd(&zstream);
	}

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::loadFromMkext(
	OSKextLogSpec   clientLogFilter,
	char          * mkextBuffer,
	uint32_t        mkextBufferLength,
	char         ** logInfoOut,
	uint32_t      * logInfoLengthOut)
{
	OSReturn         result                      = kOSReturnError;
	OSReturn         tempResult                  = kOSReturnError;

	OSSharedPtr<OSData>        mkextData;
	OSSharedPtr<OSDictionary>  mkextPlist;

	OSSharedPtr<OSArray>       logInfoArray;
	OSSharedPtr<OSSerialize>   serializer;

	OSString       * predicate                   = NULL;        // do not release
	OSDictionary   * requestArgs                 = NULL;        // do not release

	OSString       * kextIdentifier              = NULL;        // do not release
	OSNumber       * startKextExcludeNum         = NULL;        // do not release
	OSNumber       * startMatchingExcludeNum     = NULL;        // do not release
	OSBoolean      * delayAutounloadBool         = NULL;        // do not release
	OSArray        * personalityNames            = NULL;        // do not release

	/* Default values for these two options: regular autounload behavior,
	 * load all kexts, send no personalities.
	 */
	Boolean            delayAutounload           = false;
	OSKextExcludeLevel startKextExcludeLevel     = kOSKextExcludeNone;
	OSKextExcludeLevel startMatchingExcludeLevel = kOSKextExcludeAll;

	IORecursiveLockLock(sKextLock);

	if (logInfoOut) {
		*logInfoOut = NULL;
		*logInfoLengthOut = 0;
	}

	OSKext::setUserSpaceLogFilter(clientLogFilter, logInfoOut ? true : false);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogDebugLevel |
	    kOSKextLogIPCFlag,
	    "Received kext load request from user space.");

	/* Regardless of processing, the fact that we have gotten here means some
	 * user-space program is up and talking to us, so we'll switch our kext
	 * registration to reflect that.
	 */
	if (!sUserLoadsActive) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogProgressLevel |
		    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
		    "Switching to late startup (user-space) kext loading policy.");

		sUserLoadsActive = true;
	}

	if (!sLoadEnabled) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext loading is disabled.");
		result = kOSKextReturnDisabled;
		goto finish;
	}

	/* Note that we do not set a dealloc function on this OSData
	 * object! No references to it can remain after the loadFromMkext()
	 * call since we are in a MIG function, and will vm_deallocate()
	 * the buffer.
	 */
	mkextData = OSData::withBytesNoCopy(mkextBuffer,
	    mkextBufferLength);
	if (!mkextData) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogIPCFlag,
		    "Failed to create wrapper for kext load request.");
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	result = readMkext2Archive(mkextData.get(), mkextPlist, NULL);
	if (result != kOSReturnSuccess) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Failed to read kext load request.");
		goto finish;
	}

	predicate = _OSKextGetRequestPredicate(mkextPlist.get());
	if (!predicate || !predicate->isEqualTo(kKextRequestPredicateLoad)) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Received kext load request with no predicate; skipping.");
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	requestArgs = OSDynamicCast(OSDictionary,
	    mkextPlist->getObject(kKextRequestArgumentsKey));
	if (!requestArgs || !requestArgs->getCount()) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Received kext load request with no arguments.");
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	kextIdentifier = OSDynamicCast(OSString,
	    requestArgs->getObject(kKextRequestArgumentBundleIdentifierKey));

	if (!kextIdentifier) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Received kext load request with no kext identifier.");
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	startKextExcludeNum = OSDynamicCast(OSNumber,
	    requestArgs->getObject(kKextRequestArgumentStartExcludeKey));
	startMatchingExcludeNum = OSDynamicCast(OSNumber,
	    requestArgs->getObject(kKextRequestArgumentStartMatchingExcludeKey));
	delayAutounloadBool = OSDynamicCast(OSBoolean,
	    requestArgs->getObject(kKextRequestArgumentDelayAutounloadKey));
	personalityNames = OSDynamicCast(OSArray,
	    requestArgs->getObject(kKextRequestArgumentPersonalityNamesKey));

	if (delayAutounloadBool) {
		delayAutounload = delayAutounloadBool->getValue();
	}
	if (startKextExcludeNum) {
		startKextExcludeLevel = startKextExcludeNum->unsigned8BitValue();
	}
	if (startMatchingExcludeNum) {
		startMatchingExcludeLevel = startMatchingExcludeNum->unsigned8BitValue();
	}

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogIPCFlag,
	    "Received request from user space to load kext %s.",
	    kextIdentifier->getCStringNoCopy());

	/* Load the kext, with no deferral, since this is a load from outside
	 * the kernel.
	 * xxx - Would like a better way to handle the default values for the
	 * xxx - start/match opt args.
	 */
	result = OSKext::loadKextWithIdentifier(
		kextIdentifier,
		/* kextRef */ NULL,
		/* allowDefer */ false,
		delayAutounload,
		startKextExcludeLevel,
		startMatchingExcludeLevel,
		personalityNames);
	if (result != kOSReturnSuccess) {
		goto finish;
	}
	/* If the load came down from the IOKit daemon, it will shortly inform IOCatalogue
	 * for matching via a separate IOKit calldown.
	 */

finish:

	/* Gather up the collected log messages for user space. Any
	 * error messages past this call will not make it up as log messages
	 * but will be in the system log.
	 */
	logInfoArray = OSKext::clearUserSpaceLogFilter();

	if (logInfoArray && logInfoOut && logInfoLengthOut) {
		tempResult = OSKext::serializeLogInfo(logInfoArray.get(),
		    logInfoOut, logInfoLengthOut);
		if (tempResult != kOSReturnSuccess) {
			result = tempResult;
		}
	}

	OSKext::flushNonloadedKexts(/* flushPrelinkedKexts */ false);

	IORecursiveLockUnlock(sKextLock);

	/* Note: mkextDataObject will have been retained by every kext w/an
	 * executable in it. That should all have been flushed out at the
	 * and of the load operation, but you never know....
	 */
	if (mkextData && mkextData->getRetainCount() > 1) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogIPCFlag,
		    "Kext load request buffer from user space still retained by a kext; "
		    "probable memory leak.");
	}

	return result;
}

#endif // CONFIG_KXLD

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::serializeLogInfo(
	OSArray   * logInfoArray,
	char     ** logInfoOut,
	uint32_t  * logInfoLengthOut)
{
	OSReturn        result      = kOSReturnError;
	char          * buffer      = NULL;
	kern_return_t   kmem_result = KERN_FAILURE;
	OSSharedPtr<OSSerialize>  serializer;
	char         * logInfo            = NULL;        // returned by reference
	uint32_t       logInfoLength      = 0;

	if (!logInfoArray || !logInfoOut || !logInfoLengthOut) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Internal error; invalid arguments to OSKext::serializeLogInfo().");
		/* Bad programmer. */
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	serializer = OSSerialize::withCapacity(0);
	if (!serializer) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Failed to create serializer on log info for request from user space.");
		/* Incidental error; we're going to (try to) allow the request
		 * itself to succeed. */
	} else {
		if (!logInfoArray->serialize(serializer.get())) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Failed to serialize log info for request from user space.");
			/* Incidental error; we're going to (try to) allow the request
			 * itself to succeed. */
		} else {
			logInfo = serializer->text();
			logInfoLength = serializer->getLength();

			kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer, round_page(logInfoLength),
			    KMA_DATA_SHARED, VM_KERN_MEMORY_OSKEXT);
			if (kmem_result != KERN_SUCCESS) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogErrorLevel |
				    kOSKextLogIPCFlag,
				    "Failed to copy log info for request from user space.");
				/* Incidental error; we're going to (try to) allow the request
				 * to succeed. */
			} else {
				/* 11981737 - clear uninitialized data in last page */
				bzero((void *)(buffer + logInfoLength),
				    (round_page(logInfoLength) - logInfoLength));
				memcpy(buffer, logInfo, logInfoLength);
				*logInfoOut = buffer;
				*logInfoLengthOut = logInfoLength;
			}
		}
	}

	result = kOSReturnSuccess;
finish:
	return result;
}

#if PRAGMA_MARK
#pragma mark Instance Management Methods
#endif
/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::lookupKextWithIdentifier(const char * kextIdentifier)
{
	OSSharedPtr<OSKext> foundKext;

	IORecursiveLockLock(sKextLock);
	foundKext.reset(OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier)), OSRetain);
	IORecursiveLockUnlock(sKextLock);

	return foundKext;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::lookupKextWithIdentifier(OSString * kextIdentifier)
{
	return OSKext::lookupKextWithIdentifier(kextIdentifier->getCStringNoCopy());
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::lookupDextWithIdentifier(OSString * dextIdentifier, OSData *dextUniqueIdentifier)
{
	OSSharedPtr<OSKext> foundDext;
	foundDext.reset();

	IORecursiveLockLock(sKextLock);
	OSKext *dext = OSDynamicCast(OSKext, sKextsByID->getObject(dextIdentifier->getCStringNoCopy()));
	if (dext != NULL && dext->isDriverKit()) {
		if (dext->dextUniqueID == NULL || dext->dextUniqueID->isEqualTo(dextUniqueIdentifier)) {
			foundDext.reset(dext, OSRetain);
		}
	}
	IORecursiveLockUnlock(sKextLock);

	return foundDext;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::lookupKextWithLoadTag(uint32_t aTag)
{
	OSSharedPtr<OSKext> foundKext;             // returned
	uint32_t i, j;
	OSArray *list[2] = {sLoadedKexts.get(), sLoadedDriverKitKexts.get()};
	uint32_t count[2] = {sLoadedKexts->getCount(), sLoadedDriverKitKexts->getCount()};

	IORecursiveLockLock(sKextLock);

	for (j = 0; j < (sizeof(list) / sizeof(list[0])); j++) {
		for (i = 0; i < count[j]; i++) {
			OSKext * thisKext = OSDynamicCast(OSKext, list[j]->getObject(i));
			if (thisKext->getLoadTag() == aTag) {
				foundKext.reset(thisKext, OSRetain);
				goto finish;
			}
		}
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return foundKext;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::lookupKextWithAddress(vm_address_t address)
{
	OSSharedPtr<OSKext> foundKext;             // returned
	uint32_t count, i;
	kmod_info_t *kmod_info;
	vm_address_t originalAddress;
#if defined(__arm64__)
	uint64_t   textExecBase;
	size_t     textExecSize;
#endif /* defined(__arm64__) */

	originalAddress = address;
#if  __has_feature(ptrauth_calls)
	address = (vm_address_t)VM_KERNEL_STRIP_PTR(address);
#endif /*  __has_feature(ptrauth_calls) */

	IORecursiveLockLock(sKextLock);

	count = sLoadedKexts->getCount();
	for (i = 0; i < count; i++) {
		OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
		if (thisKext == sKernelKext) {
			continue;
		}
		if (thisKext->kmod_info && thisKext->kmod_info->address) {
			kmod_info = thisKext->kmod_info;
			vm_address_t kext_start = kmod_info->address;
			vm_address_t kext_end = kext_start + kmod_info->size;
			if ((kext_start <= address) && (address < kext_end)) {
				foundKext.reset(thisKext, OSRetain);
				goto finish;
			}
#if defined(__arm64__)
			textExecBase = (uintptr_t) getsegdatafromheader((kernel_mach_header_t *)kmod_info->address, "__TEXT_EXEC", &textExecSize);

			/**
			 * If the addresses within the Mach-O are unslid, then manually
			 * slide any addresses coming from the Mach-O before usage.
			 */
			if (thisKext->flags.unslidMachO) {
				textExecBase = (uintptr_t) ml_static_slide((vm_offset_t) textExecBase);
			}

			if ((textExecBase <= address) && (address < textExecBase + textExecSize)) {
				foundKext.reset(thisKext, OSRetain);
				goto finish;
			}
#endif /* defined (__arm64__) */
		}
	}
	if ((address >= vm_kernel_stext) && (address < vm_kernel_etext)) {
		foundKext.reset(sKernelKext, OSRetain);
		goto finish;
	}
	/*
	 * DriverKit userspace executables do not have a kernel linkedExecutable,
	 * so we "fake" their address range with the LoadTag. We cannot use the ptrauth-stripped address
	 * here, so use the original address passed to this method.
	 *
	 * This is supposed to be used for logging reasons only. When logd
	 * calls this function it ors the address with FIREHOSE_TRACEPOINT_PC_KERNEL_MASK, so we
	 * remove it here before checking it against the LoadTag.
	 * Also we need to remove FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT set when emitting the log line.
	 */

	address = originalAddress & ~(FIREHOSE_TRACEPOINT_PC_KERNEL_MASK | FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT);
	count = sLoadedDriverKitKexts->getCount();
	for (i = 0; i < count; i++) {
		OSKext * thisKext = OSDynamicCast(OSKext, sLoadedDriverKitKexts->getObject(i));
		if (thisKext->getLoadTag() == address) {
			foundKext.reset(thisKext, OSRetain);
		}
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return foundKext;
}

OSSharedPtr<OSData>
OSKext::copyKextUUIDForAddress(OSNumber *address)
{
	OSSharedPtr<OSData>   uuid;
	OSSharedPtr<OSKext>   kext;

	if (!address) {
		return NULL;
	}

#if CONFIG_MACF
	/* Is the calling process allowed to query kext info? */
	if (current_task() != kernel_task) {
		int macCheckResult = 0;
		kauth_cred_t cred = NULL;

		cred = kauth_cred_get_with_ref();
		macCheckResult = mac_kext_check_query(cred);
		kauth_cred_unref(&cred);

		if (macCheckResult != 0) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Failed to query kext UUID (MAC policy error 0x%x).",
			    macCheckResult);
			return NULL;
		}
	}
#endif

	uintptr_t slidAddress = ml_static_slide((uintptr_t)address->unsigned64BitValue());
	if (slidAddress != 0) {
		kext = lookupKextWithAddress(slidAddress);
		if (kext) {
			uuid = kext->copyTextUUID();
		}
	}

	if (!uuid) {
		/*
		 * If we still don't have a UUID, then we failed to match the slid + stripped address with
		 * a kext. This might have happened because the log message came from a dext.
		 *
		 * Try again with the original address.
		 */
		kext = lookupKextWithAddress((vm_address_t)address->unsigned64BitValue());
		if (kext && kext->isDriverKit()) {
			uuid = kext->copyTextUUID();
		}
	}

	return uuid;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSKext>
OSKext::lookupKextWithUUID(uuid_t wanted)
{
	OSSharedPtr<OSKext> foundKext;             // returned
	uint32_t j, i;
	OSArray *list[2] = {sLoadedKexts.get(), sLoadedDriverKitKexts.get()};
	uint32_t count[2] = {sLoadedKexts->getCount(), sLoadedDriverKitKexts->getCount()};


	IORecursiveLockLock(sKextLock);

	for (j = 0; j < (sizeof(list) / sizeof(list[0])); j++) {
		for (i = 0; i < count[j]; i++) {
			OSKext   * thisKext     = NULL;

			thisKext = OSDynamicCast(OSKext, list[j]->getObject(i));
			if (!thisKext) {
				continue;
			}

			OSSharedPtr<OSData> uuid_data = thisKext->copyUUID();
			if (!uuid_data) {
				continue;
			}

			uuid_t uuid;
			memcpy(&uuid, uuid_data->getBytesNoCopy(), sizeof(uuid));

			if (0 == uuid_compare(wanted, uuid)) {
				foundKext.reset(thisKext, OSRetain);
				goto finish;
			}
		}
	}
finish:
	IORecursiveLockUnlock(sKextLock);

	return foundKext;
}




/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::isKextWithIdentifierLoaded(const char * kextIdentifier)
{
	bool result = false;
	OSKext * foundKext = NULL;             // returned

	IORecursiveLockLock(sKextLock);

	foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
	if (foundKext && foundKext->isLoaded()) {
		result = true;
	}

	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
* xxx - should spawn a separate thread so a kext can safely have
* xxx - itself unloaded.
*********************************************************************/
/* static */
OSReturn
OSKext::removeKext(
	OSKext * aKext,
#if CONFIG_EMBEDDED
	__unused
#endif
	bool     terminateServicesAndRemovePersonalitiesFlag)
{
#if CONFIG_EMBEDDED
	if (!aKext->isDriverKit()) {
		OSKextLog(aKext,
		    kOSKextLogErrorLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "removeKext() called for %s, only supported on embedded for DriverKit dexts",
		    aKext->getIdentifier() ? aKext->getIdentifierCString() : "unknown kext");

		return kOSReturnSuccess;
	}
#endif /* CONFIG_EMBEDDED */
	OSReturn result    = kOSKextReturnInUse;
	OSKext * checkKext = NULL;         // do not release
#if CONFIG_MACF
	int macCheckResult = 0;
	kauth_cred_t cred  = NULL;
#endif

	IORecursiveLockLock(sKextLock);

	/* If the kext has no identifier, it failed to init
	 * so isn't in sKextsByID and it isn't loaded.
	 */
	if (!aKext->getIdentifier()) {
		result = kOSReturnSuccess;
		goto finish;
	}

	checkKext = OSDynamicCast(OSKext,
	    sKextsByID->getObject(aKext->getIdentifier()));
	if (checkKext != aKext) {
		result = kOSKextReturnNotFound;
		goto finish;
	}

	if (aKext->isLoaded() || aKext->isDriverKit()) {
#if CONFIG_MACF
		if (current_task() != kernel_task) {
			cred = kauth_cred_get_with_ref();
			macCheckResult = mac_kext_check_unload(cred, aKext->getIdentifierCString());
			kauth_cred_unref(&cred);
		}

		if (macCheckResult != 0) {
			result = kOSReturnError;
			OSKextLog(aKext,
			    kOSKextLogErrorLevel |
			    kOSKextLogKextBookkeepingFlag,
			    "Failed to remove kext %s (MAC policy error 0x%x).",
			    aKext->getIdentifierCString(), macCheckResult);
			goto finish;
		}
#endif

		/* make sure there are no resource requests in flight - 17187548 */
		if (aKext->declaresExecutable() && aKext->countRequestCallbacks()) {
			goto finish;
		}
		if (aKext->flags.unloadUnsupported) {
			result = kOSKextReturnInUse;
			OSKextLog(aKext,
			    kOSKextLogErrorLevel |
			    kOSKextLogKextBookkeepingFlag,
			    "Can't remove kext %s; unsupported by cache.",
			    aKext->getIdentifierCString());
			goto finish;
		}

		/* If we are terminating, send the request to the IOCatalogue
		 * (which will actually call us right back but that's ok we have
		 * a recursive lock don't you know) but do not ask the IOCatalogue
		 * to call back with an unload, we'll do that right here.
		 */
		if (terminateServicesAndRemovePersonalitiesFlag) {
			result = gIOCatalogue->terminateDriversForModule(
				aKext->getIdentifierCString(), /* unload */ false);
			if (result != kOSReturnSuccess) {
				OSKextLog(aKext,
				    kOSKextLogErrorLevel |
				    kOSKextLogKextBookkeepingFlag,
				    "Can't remove kext %s; services failed to terminate - 0x%x.",
				    aKext->getIdentifierCString(), result);
				goto finish;
			}
		}

		result = aKext->unload();
		if (result != kOSReturnSuccess) {
			OSKextLog(aKext,
			    kOSKextLogErrorLevel |
			    kOSKextLogKextBookkeepingFlag,
			    "Can't remove kext %s; kext failed to unload - 0x%x.",
			    aKext->getIdentifierCString(), result);
			goto finish;
		}
	}

	/* Remove personalities as requested. This is a bit redundant for a loaded
	 * kext as IOCatalogue::terminateDriversForModule() removes driver
	 * personalities, but it doesn't restart matching, which we always want
	 * coming from here, and OSKext::removePersonalitiesFromCatalog() ensures
	 * that happens.
	 */
	if (terminateServicesAndRemovePersonalitiesFlag) {
		aKext->removePersonalitiesFromCatalog();
	}

	if (aKext->isInFileset()) {
		OSKextLog(aKext,
		    kOSKextLogProgressLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Fileset kext %s unloaded.",
		    aKext->getIdentifierCString());
	} else {
		OSKextLog(aKext,
		    kOSKextLogProgressLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Removing kext %s.",
		    aKext->getIdentifierCString());

		sKextsByID->removeObject(aKext->getIdentifier());
	}
	result = kOSReturnSuccess;

finish:
	IORecursiveLockUnlock(sKextLock);
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::removeKextWithIdentifier(
	const char * kextIdentifier,
	bool         terminateServicesAndRemovePersonalitiesFlag)
{
	OSReturn result = kOSReturnError;

	IORecursiveLockLock(sKextLock);

	OSKext * aKext = OSDynamicCast(OSKext,
	    sKextsByID->getObject(kextIdentifier));
	if (!aKext) {
		result = kOSKextReturnNotFound;
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Can't remove kext %s - not found.",
		    kextIdentifier);
		goto finish;
	}

	result = OSKext::removeKext(aKext,
	    terminateServicesAndRemovePersonalitiesFlag);

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::removeKextWithLoadTag(
	OSKextLoadTag loadTag,
	bool          terminateServicesAndRemovePersonalitiesFlag)
{
	OSReturn result    = kOSReturnError;
	OSKext * foundKext = NULL;
	uint32_t i, j;
	OSArray *list[2] = {sLoadedKexts.get(), sLoadedDriverKitKexts.get()};
	uint32_t count[2] = {sLoadedKexts->getCount(), sLoadedDriverKitKexts->getCount()};


	IORecursiveLockLock(sKextLock);

	for (j = 0; j < (sizeof(list) / sizeof(list[0])); j++) {
		for (i = 0; i < count[j]; i++) {
			OSKext * thisKext = OSDynamicCast(OSKext, list[j]->getObject(i));
			if (thisKext->loadTag == loadTag) {
				foundKext = thisKext;
				break;
			}
		}
	}

	if (!foundKext) {
		result = kOSKextReturnNotFound;
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Can't remove kext with load tag %d - not found.",
		    loadTag);
		goto finish;
	}

	result = OSKext::removeKext(foundKext,
	    terminateServicesAndRemovePersonalitiesFlag);

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSDictionary>
OSKext::copyKexts(void)
{
	OSSharedPtr<OSDictionary> result;

	IORecursiveLockLock(sKextLock);
	result = OSDynamicPtrCast<OSDictionary>(sKextsByID->copyCollection());
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
#define BOOTER_KEXT_PREFIX   "Driver-"

typedef struct _DeviceTreeBuffer {
	uint32_t paddr;
	uint32_t length;
} _DeviceTreeBuffer;

/*********************************************************************
* Create a dictionary of excluded kexts from the given booter data.
*********************************************************************/
/* static */
void
OSKext::createExcludeListFromBooterData(
	OSDictionary *          theDictionary,
	OSCollectionIterator *  theIterator )
{
	OSString                  * deviceTreeName      = NULL;        // do not release
	const _DeviceTreeBuffer   * deviceTreeBuffer    = NULL;        // do not release
	char                      * booterDataPtr       = NULL;        // do not release
	_BooterKextFileInfo       * kextFileInfo        = NULL;        // do not release
	char                      * infoDictAddr        = NULL;        // do not release
	OSSharedPtr<OSObject>       parsedXML;
	OSDictionary              * theInfoDict         = NULL;        // do not release

	theIterator->reset();

	/* look for AppleKextExcludeList.kext */
	while ((deviceTreeName =
	    OSDynamicCast(OSString, theIterator->getNextObject()))) {
		const char *    devTreeNameCString;
		OSData *        deviceTreeEntry;        // do not release
		OSString *      myBundleID;        // do not release

		deviceTreeEntry =
		    OSDynamicCast(OSData, theDictionary->getObject(deviceTreeName));
		if (!deviceTreeEntry) {
			continue;
		}

		/* Make sure it is a kext */
		devTreeNameCString = deviceTreeName->getCStringNoCopy();
		if (strncmp(devTreeNameCString, BOOTER_KEXT_PREFIX,
		    (sizeof(BOOTER_KEXT_PREFIX) - 1)) != 0) {
			OSKextLog(NULL,
			    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
			    "\"%s\" not a kext",
			    devTreeNameCString);
			continue;
		}

		deviceTreeBuffer = (const _DeviceTreeBuffer *)
		    deviceTreeEntry->getBytesNoCopy(0, sizeof(deviceTreeBuffer));
		if (!deviceTreeBuffer) {
			continue;
		}

		booterDataPtr = (char *)ml_static_ptovirt(deviceTreeBuffer->paddr);
		if (!booterDataPtr) {
			continue;
		}

		kextFileInfo = (_BooterKextFileInfo *) booterDataPtr;
		if (!kextFileInfo->infoDictPhysAddr ||
		    !kextFileInfo->infoDictLength) {
			continue;
		}

		infoDictAddr = (char *)
		    ml_static_ptovirt(kextFileInfo->infoDictPhysAddr);
		if (!infoDictAddr) {
			continue;
		}

		parsedXML = OSUnserializeXML(infoDictAddr);
		if (!parsedXML) {
			continue;
		}

		theInfoDict = OSDynamicCast(OSDictionary, parsedXML.get());
		if (!theInfoDict) {
			continue;
		}

		myBundleID =
		    OSDynamicCast(OSString,
		    theInfoDict->getObject(kCFBundleIdentifierKey));
		if (myBundleID &&
		    strcmp( myBundleID->getCStringNoCopy(), kIOExcludeListBundleID ) == 0) {
			boolean_t updated = updateExcludeList(theInfoDict);
			if (!updated) {
				/* 25322874 */
				panic("Missing OSKextExcludeList dictionary");
			}
			break;
		}
	}         // while ( (deviceTreeName = ...) )

	return;
}

/*********************************************************************
* Create a dictionary of excluded kexts from the given prelink
* info (kernelcache).
*********************************************************************/
/* static */
void
OSKext::createExcludeListFromPrelinkInfo( OSArray * theInfoArray )
{
	OSDictionary *  myInfoDict = NULL;        // do not release
	OSString *      myBundleID;        // do not release
	u_int           i;

	/* Find the Apple Kext Exclude List. */
	for (i = 0; i < theInfoArray->getCount(); i++) {
		myInfoDict = OSDynamicCast(OSDictionary, theInfoArray->getObject(i));
		if (!myInfoDict) {
			continue;
		}
		myBundleID =
		    OSDynamicCast(OSString,
		    myInfoDict->getObject(kCFBundleIdentifierKey));
		if (myBundleID &&
		    strcmp( myBundleID->getCStringNoCopy(), kIOExcludeListBundleID ) == 0) {
			boolean_t updated = updateExcludeList(myInfoDict);
			if (!updated) {
				/* 25322874 */
				panic("Missing OSKextExcludeList dictionary");
			}
			break;
		}
	}         // for (i = 0; i < theInfoArray->getCount()...

	return;
}

/* static */
boolean_t
OSKext::updateExcludeList(OSDictionary *infoDict)
{
	OSDictionary *myTempDict = NULL;         // do not free
	OSString     *myTempString = NULL;        // do not free
	OSKextVersion newVersion = 0;
	boolean_t updated = false;

	if (!infoDict) {
		return false;
	}

	myTempDict = OSDynamicCast(OSDictionary, infoDict->getObject("OSKextExcludeList"));
	if (!myTempDict) {
		return false;
	}

	myTempString = OSDynamicCast(OSString, infoDict->getObject(kCFBundleVersionKey));
	if (!myTempString) {
		return false;
	}

	newVersion = OSKextParseVersionString(myTempString->getCStringNoCopy());
	if (newVersion == 0) {
		return false;
	}

	IORecursiveLockLock(sKextLock);

	if (newVersion > sExcludeListVersion) {
		sExcludeListByID = OSDictionary::withDictionary(myTempDict, 0);
		sExcludeListVersion = newVersion;
		updated = true;
	}

	IORecursiveLockUnlock(sKextLock);
	return updated;
}

#if PRAGMA_MARK
#pragma mark Accessors
#endif

/*********************************************************************
*********************************************************************/
const OSObject *
OSKext::getBundleExecutable(void)
{
	return infoDict->getObject(kCFBundleExecutableKey);
}

/*********************************************************************
*********************************************************************/
const OSSymbol *
OSKext::getIdentifier(void)
{
	return bundleID.get();
}

/*********************************************************************
* A kext must have a bundle identifier to even survive initialization;
* this is guaranteed to exist past then.
*********************************************************************/
const char *
OSKext::getIdentifierCString(void)
{
	return bundleID->getCStringNoCopy();
}

/*********************************************************************
*********************************************************************/
OSKextVersion
OSKext::getVersion(void)
{
	return version;
}

/*********************************************************************
*********************************************************************/
OSKextVersion
OSKext::getCompatibleVersion(void)
{
	return compatibleVersion;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isLibrary(void)
{
	return getCompatibleVersion() > 0;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isCompatibleWithVersion(OSKextVersion aVersion)
{
	if ((compatibleVersion > -1 && version > -1) &&
	    (compatibleVersion <= version && aVersion <= version)) {
		return true;
	}
	return false;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::declaresExecutable(void)
{
	if (isDriverKit()) {
		return false;
	}
	return getPropertyForHostArch(kCFBundleExecutableKey) != NULL;
}

/*********************************************************************
*********************************************************************/
OSData *
OSKext::getExecutable(void)
{
	OSData * result              = NULL;
	OSSharedPtr<OSData> extractedExecutable;

	if (flags.builtin) {
		return sKernelKext->linkedExecutable.get();
	}

	result = OSDynamicCast(OSData, infoDict->getObject(_kOSKextExecutableKey));
	if (result) {
		return result;
	}

#if CONFIG_KXLD
	OSData * mkextExecutableRef  = NULL;        // do not release
	mkextExecutableRef = OSDynamicCast(OSData,
	    getPropertyForHostArch(_kOSKextMkextExecutableReferenceKey));

	if (mkextExecutableRef) {
		MkextEntryRef * mkextEntryRef = (MkextEntryRef *)
		    mkextExecutableRef->getBytesNoCopy();
		uint32_t mkextVersion = MKEXT_GET_VERSION(mkextEntryRef->mkext);
		if (mkextVersion == MKEXT_VERS_2) {
			mkext2_file_entry * fileinfo =
			    (mkext2_file_entry *)mkextEntryRef->fileinfo;
			uint32_t compressedSize = MKEXT2_GET_ENTRY_COMPSIZE(fileinfo);
			uint32_t fullSize = MKEXT2_GET_ENTRY_FULLSIZE(fileinfo);
			extractedExecutable = extractMkext2FileData(
				MKEXT2_GET_ENTRY_DATA(fileinfo), "executable",
				compressedSize, fullSize);
		} else {
			OSKextLog(this, kOSKextLogErrorLevel |
			    kOSKextLogArchiveFlag,
			    "Kext %s - unknown mkext version 0x%x for executable.",
			    getIdentifierCString(), mkextVersion);
		}

		/* Regardless of success, remove the mkext executable,
		 * and drop one reference on the mkext.  (setExecutable() does not
		 * replace, it removes, or panics if asked to replace.)
		 */
		infoDict->removeObject(_kOSKextMkextExecutableReferenceKey);
		infoDict->removeObject(_kOSKextExecutableExternalDataKey);

		if (extractedExecutable && extractedExecutable->getLength()) {
			if (!setExecutable(extractedExecutable.get())) {
				goto finish;
			}
			result = extractedExecutable.get();
		} else {
			goto finish;
		}
	}

finish:
#endif // CONFIG_KXLD
	return result;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isInterface(void)
{
	return flags.interface;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isKernel(void)
{
	return this == sKernelKext;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isKernelComponent(void)
{
	return flags.kernelComponent ? true : false;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isExecutable(void)
{
	return !isKernel() && !isInterface() && declaresExecutable();
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isSpecialKernelBinary(void)
{
#if CONFIG_SPTM
	return (this->kmod_info) &&
	       ((this->kmod_info->id == kOSKextSPTMLoadTag) ||
	       (this->kmod_info->id == kOSKextTXMLoadTag));
#else
	return false;
#endif
}

/*********************************************************************
* We might want to check this recursively for all dependencies,
* since a subtree of dependencies could get loaded before we hit
* a dependency that isn't safe-boot-loadable.
*
* xxx - Might want to return false if OSBundleEnableKextLogging or
* OSBundleDebugLevel
* or IOKitDebug is nonzero too (we used to do that, but I don't see
* the point except it's usually development drivers, which might
* cause panics on startup, that have those properties). Heh; could
* use a "kx" boot-arg!
*********************************************************************/
bool
OSKext::isLoadableInSafeBoot(void)
{
	bool       result   = false;
	OSString * required = NULL;         // do not release

	if (isKernel()) {
		result = true;
		goto finish;
	}

	if (isDriverKit()) {
		result = true;
		goto finish;
	}

	required = OSDynamicCast(OSString,
	    getPropertyForHostArch(kOSBundleRequiredKey));
	if (!required) {
		goto finish;
	}
	if (required->isEqualTo(kOSBundleRequiredRoot) ||
	    required->isEqualTo(kOSBundleRequiredLocalRoot) ||
	    required->isEqualTo(kOSBundleRequiredNetworkRoot) ||
	    required->isEqualTo(kOSBundleRequiredSafeBoot) ||
	    required->isEqualTo(kOSBundleRequiredConsole)) {
		result = true;
	}

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isPrelinked(void)
{
	return flags.prelinked ? true : false;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isLoaded(void)
{
	return flags.loaded ? true : false;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isStarted(void)
{
	return flags.started ? true : false;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::isCPPInitialized(void)
{
	return flags.CPPInitialized;
}

/*********************************************************************
*********************************************************************/
void
OSKext::setCPPInitialized(bool initialized)
{
	flags.CPPInitialized = initialized;
}

/*********************************************************************
*********************************************************************/
uint32_t
OSKext::getLoadTag(void)
{
	return loadTag;
}

/*********************************************************************
*********************************************************************/
void
OSKext::getSizeInfo(uint32_t *loadSize, uint32_t *wiredSize)
{
	if (linkedExecutable) {
		*loadSize = linkedExecutable->getLength();

		/* If we have a kmod_info struct, calculated the wired size
		 * from that. Otherwise it's the full load size.
		 */
		if (kmod_info) {
			*wiredSize = *loadSize - (uint32_t)kmod_info->hdr_size;
		} else {
			*wiredSize = *loadSize;
		}
	} else {
		*wiredSize = 0;
		*loadSize = 0;
	}
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSData>
OSKext::copyUUID(void)
{
	OSSharedPtr<OSData>          result;
	OSData                     * theExecutable = NULL;        // do not release
	const kernel_mach_header_t * header;

	/* An interface kext doesn't have a linked executable with an LC_UUID,
	 * we create one when it's linked.
	 */
	if (interfaceUUID) {
		result = interfaceUUID;
		goto finish;
	}

	if (flags.builtin || isInterface()) {
		return sKernelKext->copyUUID();
	}

	if (isDriverKit() && infoDict) {
		return driverKitUUID;
	}

	/* For real kexts, try to get the UUID from the linked executable,
	 * or if is hasn't been linked yet, the unrelocated executable.
	 */
	theExecutable = linkedExecutable.get();
	if (!theExecutable) {
		theExecutable = getExecutable();
	}

	if (!theExecutable) {
		goto finish;
	}

	header = (const kernel_mach_header_t *)theExecutable->getBytesNoCopy();
	result = copyMachoUUID(header);

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSData>
OSKext::copyTextUUID(void)
{
	if (flags.builtin) {
		return copyMachoUUID((const kernel_mach_header_t *)kmod_info->address);
	}
	return copyUUID();
}

/*********************************************************************
*********************************************************************/
OSSharedPtr<OSData>
OSKext::copyMachoUUID(const kernel_mach_header_t * header)
{
	OSSharedPtr<OSData>                     result;
	const struct load_command  * load_cmd      = NULL;
	const struct uuid_command  * uuid_cmd      = NULL;
	uint32_t                     i;

	load_cmd = (const struct load_command *)&header[1];

	if (header->magic != MH_MAGIC_KERNEL) {
		OSKextLog(NULL,
		    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
		    "%s: bad header %p",
		    __func__,
		    header);
		goto finish;
	}

	for (i = 0; i < header->ncmds; i++) {
		if (load_cmd->cmd == LC_UUID) {
			uuid_cmd = (struct uuid_command *)load_cmd;
			result = OSData::withValue(uuid_cmd->uuid);
			goto finish;
		}
		load_cmd = (struct load_command *)((caddr_t)load_cmd + load_cmd->cmdsize);
	}

finish:
	return result;
}

void
OSKext::setDriverKitUUID(OSData *uuid)
{
	if (!OSCompareAndSwapPtr(nullptr, uuid, &driverKitUUID)) {
		OSSafeReleaseNULL(uuid);
	}
}

OSData *
OSKext::getDextUniqueID(void)
{
	if (isDriverKit() && dextUniqueID != NULL) {
		return dextUniqueID.get();
	}

	return NULL;
}

/*
 * In case a DextUniqueID exists this function returns
 * an allocated char* with the hexadecimal represantition of
 * DextUniqueID.
 * The returned pinter needs to be freed with kfree_data, the
 * size of the allocated buffer is returned in size.
 */
static const char *
getDextUniqueIDCString(OSData *dextUniqueID, unsigned int *size)
{
	if (dextUniqueID != NULL) {
		char *s_buffer = NULL;
		unsigned int d_length = dextUniqueID->getLength();
		/*
		 * We are converting in hex, so for every byte we will have
		 * 2 hex chars and one last \0.
		 */
		unsigned int s_length = d_length * 2 + 1;
		s_buffer = (char *) kalloc_data(s_length, Z_WAITOK_ZERO);

		char *uid = (char*) dextUniqueID->getBytesNoCopy();
		int cpos = 0;
		for (unsigned int i = 0; i < d_length && cpos < (s_length - 1); i++) {
			int ret = snprintf(s_buffer + cpos, s_length - cpos - 1, "%02X", uid[i]);
			if (ret <= 0) {
				break;
			}
			cpos += ret;
		}
		*size = s_length;

		return s_buffer;
	}

	return NULL;
}

/*
 * Atomically swaps the olddext with newdext.
 * olddext will be unloaded, so it might be freed
 * after this call unless it was previously retained.
 *
 * If newdext is NULL, this unloads olddext and does not perform an upgrade
 */
void
OSKext::replaceDextInternal(OSKext *olddext, OSKext *newdext)
{
	OSReturn result;
	const OSSymbol * dextID = olddext->getIdentifier();
	OSData * oldDextUniqueIdentifier = olddext->getDextUniqueID();
	OSSharedPtr<OSArray> new_personalities;
	OSSharedPtr<OSString> kextIdentifier;
	__assert_only bool lock_held = IORecursiveLockHaveLock(sKextLock);
	assert(lock_held);

	// The old dext will be unloaded and release dextID, so we need to retain dextID here
	dextID->retain();

	if (newdext != NULL) {
		__assert_only bool eq = dextID->isEqualTo(newdext->getIdentifier());
		assert(eq);
	}

	if (newdext != NULL) {
		/*
		 * Swap the catalog personalities.
		 */
		new_personalities = newdext->copyPersonalitiesArray();
		olddext->updatePersonalitiesInCatalog(new_personalities.get());
	}

	if (NULL != oldDextUniqueIdentifier) {
		oldDextUniqueIdentifier->retain();
	}

	/*
	 * Unload the dext.
	 */
	result = olddext->unload();
	if (result != kOSReturnSuccess) {
		OSKextLog(NULL,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Cannot unload dext for upgrade %s: %d\n",
		    dextID->getCStringNoCopy(), result);
	}

	if (newdext != NULL) {
		/*
		 * Swap the dexts on the OSKext dictionary.
		 * This might free the dext.
		 */
		sKextsByID->setObject(dextID, newdext);
	} else {
		/*
		 * Remove the old dext
		 */
		removeKext(olddext, true);
	}

	/*
	 * Inform userspace.
	 */
	if (newdext != NULL) {
		result = notifyDextUpgrade(OSDynamicCast(OSString, dextID), newdext->getDextUniqueID());
		if (result != kOSReturnSuccess) {
			OSKextLog(NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Cannot send upgrade notification for %s\n",
			    dextID->getCStringNoCopy());
		}
	} else {
		// notify dext removal
		queueKextNotification(kKextRequestPredicateUnloadNotification,
		    OSDynamicCast(OSString, dextID), oldDextUniqueIdentifier);
	}

	OSSafeReleaseNULL(dextID);
	OSSafeReleaseNULL(oldDextUniqueIdentifier);
}

/*
 * To be called with sKextLock held.
 * NOTE: this could unload the olddext.
 */
bool
OSKext::upgradeDext(OSKext *olddext, OSKext *newdext)
{
	const char * dextIDCS = newdext->getIdentifierCString();
	__assert_only bool old_isDext = olddext->isDriverKit();
	__assert_only bool new_isDext = newdext->isDriverKit();
	__assert_only bool lock_held = IORecursiveLockHaveLock(sKextLock);

	assert(old_isDext && new_isDext);
	assert(lock_held);

	/*
	 * New dext and old dext have the same ID.
	 * We use this ID as key on the OSKext
	 * dictionarys/arrays.
	 */
	const OSSymbol * dextID = newdext->getIdentifier();
	__assert_only bool eq = dextID->isEqualTo(olddext->getIdentifier());
	assert(eq);

	/*
	 * Set this OSKect as to update.
	 * Note that this flags will never be removed once set.
	 * When a OSKext is marked, it will be substitued by a new
	 * OSKext, and every subsystem having a reference on this
	 * OSKext need to know they have check if they can use
	 * this OSKext or look for a new one.
	 */
	olddext->flags.dextToReplace = 1;

	/*
	 * Check if the current OSKext has any
	 * userspace processes launched.
	 * In this case we cannot upgrade and we have to
	 * delay the upgrade until all processes
	 * are done.
	 */
	if (olddext->dextLaunchedCount == 0) {
		/*
		 * Be sure that if there are no launched dexts, no
		 * pending upgrades exist.
		 * This is an error if it happens, as the decrement
		 * should have removed the dext from sDriverKitToUpgradeByID
		 * in case it reached 0.
		 */
		OSObject *pending_upgdare = sDriverKitToUpgradeByID->getObject(dextID);
		if (pending_upgdare != NULL) {
			OSKextLog(NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Pending upgrade found for %s but dextLaunchedCount is 0!\n",
			    dextIDCS);
			goto out;
		}

		replaceDextInternal(olddext, newdext);
		return true;
	}

out:

	/*
	 * Delay the upgrade.
	 * Make the new dext available in sDriverKitToUpgradeByID.
	 * In case there was already a pending upgrade, this will
	 * overwrite it.
	 */
	sDriverKitToUpgradeByID->setObject(dextID, newdext);
	return false;
}

/*
 * To be called with sKextLock held.
 * NOTE: this could unload the dext.
 */
bool
OSKext::removeDext(OSKext *dext)
{
	__assert_only bool dext_isDext = dext->isDriverKit();
	__assert_only bool lock_held = IORecursiveLockHaveLock(sKextLock);
	IOReturn result;

	assert(dext_isDext);
	assert(lock_held);

	/*
	 * Set this OSKext to be unloaded when all running instances exit.
	 */
	dext->flags.dextToReplace = 1;

	result = gIOCatalogue->terminateDriversForModule(
		dext->getIdentifierCString(), /* unload */ false, /* asynchronous */ true);
	if (result != kOSReturnSuccess) {
		OSKextLog(dext,
		    kOSKextLogErrorLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "%s services failed to terminate - 0x%x.",
		    dext->getIdentifierCString(), result);
	}

	dext->removePersonalitiesFromCatalog();
	sDriverKitToUpgradeByID->removeObject(dext->getIdentifier());

	/*
	 * Check if the current OSKext has any
	 * userspace processes launched.
	 * In this case we cannot unload and we have to
	 * delay the unload until all processes
	 * are done.
	 */
	if (dext->dextLaunchedCount == 0) {
		replaceDextInternal(dext, NULL);
		return true;
	}

	return false;
}

bool
OSKext::incrementDextLaunchCount(OSKext *dext, OSData *dextUniqueIDToMatch)
{
	bool ret = false;
	__assert_only bool isDext = dext->isDriverKit();
	assert(isDext);

	const char * dextIDCS = dext->getIdentifierCString();
	OSData *myDextUniqueID = dext->getDextUniqueID();

	if (!myDextUniqueID || !dextUniqueIDToMatch) {
		OSKextLog(dext,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Cannot find dext UniqueID for %s, cannot increment dext launches\n",
		    dextIDCS);
		return ret;
	}

	unsigned int dextUniqueIDCStringSize = 0, dextUniqueIDToMatchCStringSize = 0;
	const char *dextUniqueIDCString = getDextUniqueIDCString(myDextUniqueID, &dextUniqueIDCStringSize);
	const char *dextUniqueIDToMatchCString = getDextUniqueIDCString(dextUniqueIDToMatch, &dextUniqueIDToMatchCStringSize);
	assert(dextUniqueIDCString != NULL);
	assert(dextUniqueIDToMatchCString != NULL);

	IORecursiveLockLock(sKextLock);

	/*
	 * Check that the dext we are referencing is the same
	 * looked for the match.
	 */
	if (myDextUniqueID->isEqualTo(dextUniqueIDToMatch)) {
		if (dext->flags.dextToReplace == 0 || dext->dextLaunchedCount > 0) {
			if (dext->dextLaunchedCount == kOSKextMaxDextLaunchedCount) {
				OSKextLog(dext,
				    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
				    "Too many dexts launched for %s UniqueID %s\n",
				    dextIDCS, dextUniqueIDCString);
			} else {
				dext->dextLaunchedCount++;
				ret = true;

				OSKextLog(dext,
				    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
				    "New dext launched for  %s UniqueID %s",
				    dextIDCS, dextUniqueIDCString);
			}
		} else {
			OSKextLog(dext,
			    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
			    "Dext %s UniqueID %s requires update, cannot launch a new dext\n",
			    dextIDCS, dextUniqueIDCString);
		}
	} else {
		OSKextLog(dext,
		    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
		    "Dext %s: UniqueID %s does not match UniqueID looked for %s, cannot launch a new dext\n",
		    dextIDCS, dextUniqueIDCString, dextUniqueIDToMatchCString);
	}

	IORecursiveLockUnlock(sKextLock);

	if (dextUniqueIDCString != NULL) {
		kfree_data(dextUniqueIDCString, dextUniqueIDCStringSize);
	}
	if (dextUniqueIDToMatchCString != NULL) {
		kfree_data(dextUniqueIDToMatchCString, dextUniqueIDToMatchCStringSize);
	}
	return ret;
}

bool
OSKext::decrementDextLaunchCount(OSString *bundleID)
{
	bool ret = false;
	const char * dextIDCS;
	OSData *myDextUniqueID;
	unsigned int dextUniqueIDCStringSize = 0;
	const char * dextUniqueIDCString = NULL;
	OSKext* dext = NULL;

	if (!bundleID) {
		return ret;
	}
	dextIDCS = bundleID->getCStringNoCopy();

	IORecursiveLockLock(sKextLock);

	/*
	 * Look for the dext with the bundle it. This
	 * call is triggered only if a previous increment was
	 * performed. It means that the dext could have not
	 * been upgraded as its dextLaunchedCount was at least 1.
	 * Because of this it still needs to be available
	 * in sKextsByID.
	 */
	dext = OSDynamicCast(OSKext, sKextsByID->getObject(dextIDCS));
	if (!dext || !dext->isDriverKit()) {
		OSKextLog(NULL,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Cannot find dext for %s, cannot decrement dext launches\n",
		    dextIDCS);

		goto out_locked;
	}

	myDextUniqueID = dext->getDextUniqueID();
	if (!myDextUniqueID) {
		OSKextLog(dext,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Cannot find dext UniqueID for %s, cannot decrement dext launches\n",
		    dextIDCS);

		goto out_locked;
	}
	dextUniqueIDCString = getDextUniqueIDCString(myDextUniqueID, &dextUniqueIDCStringSize);
	assert(dextUniqueIDCString != NULL);

	if (dext->dextLaunchedCount == 0) {
		OSKextLog(dext,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Over decrementing dext launch for %s UniqueID %s\n",
		    dextIDCS, dextUniqueIDCString);

		goto out_locked;
	}

	dext->dextLaunchedCount--;

	OSKextLog(dext,
	    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
	    "Dext terminated for %s UniqueID %s",
	    dextIDCS, dextUniqueIDCString);

	if (dext->dextLaunchedCount == 0 && dext->flags.dextToReplace == 1) {
		/*
		 * Find the upgraded dext.
		 */
		OSKext *newdext = OSDynamicCast(OSKext, sDriverKitToUpgradeByID->getObject(dextIDCS));
		if (newdext) {
			OSKextLog(dext,
			    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
			    "Dext upgrade for %s UniqueID %s",
			    dextIDCS, dextUniqueIDCString);
			replaceDextInternal(dext, newdext);
			/* NOTE dext could have been freed past this point */

			sDriverKitToUpgradeByID->removeObject(dextIDCS);
		} else {
			OSKextLog(dext,
			    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
			    "Dext unload for %s UniqueID %s",
			    dextIDCS, dextUniqueIDCString);
			replaceDextInternal(dext, NULL);
		}

		ret = true;
	}
out_locked:
	IORecursiveLockUnlock(sKextLock);

	if (dextUniqueIDCString != NULL) {
		kfree_data(dextUniqueIDCString, dextUniqueIDCStringSize);
	}

	return ret;
}

/*********************************************************************
*********************************************************************/
#if defined (__arm__)
#include <arm/arch.h>
#endif

#if   defined (__x86_64__)
#define ARCHNAME "x86_64"
#elif defined (__arm64__)
#define ARCHNAME "arm64"
#elif defined (__arm__)

#if defined (__ARM_ARCH_7S__)
#define ARCHNAME "armv7s"
#elif defined (__ARM_ARCH_7F__)
#define ARCHNAME "armv7f"
#elif defined (__ARM_ARCH_7K__)
#define ARCHNAME "armv7k"
#elif defined (_ARM_ARCH_7) /* umbrella for all remaining */
#define ARCHNAME "armv7"
#elif defined (_ARM_ARCH_6) /* umbrella for all armv6 */
#define ARCHNAME "armv6"
#endif

#elif defined (__arm64__)
#define ARCHNAME "arm64"
#else
#error architecture not supported
#endif

#define ARCH_SEPARATOR_CHAR  '_'

static char *
makeHostArchKey(const char * key, size_t * keySizeOut)
{
	char     * result = NULL;
	size_t     keyLength = strlen(key);
	size_t     keySize;

	/* Add 1 for the ARCH_SEPARATOR_CHAR, and 1 for the '\0'.
	 */
	keySize = 1 + 1 + keyLength + strlen(ARCHNAME);
	result = (char *)kalloc_data_tag(keySize, Z_WAITOK, VM_KERN_MEMORY_OSKEXT);

	if (!result) {
		goto finish;
	}
	strlcpy(result, key, keySize);
	result[keyLength++] = ARCH_SEPARATOR_CHAR;
	result[keyLength] = '\0';
	strlcat(result, ARCHNAME, keySize);
	*keySizeOut = keySize;

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
OSObject *
OSKext::getPropertyForHostArch(const char * key)
{
	OSObject * result           = NULL;// do not release
	size_t     hostArchKeySize  = 0;
	char     * hostArchKey      = NULL;// must kfree

	if (!key || !infoDict) {
		goto finish;
	}

	/* Some properties are not allowed to be arch-variant:
	 * - Any CFBundle... property.
	 * - OSBundleIsInterface.
	 * - OSKernelResource.
	 */
	if (STRING_HAS_PREFIX(key, "OS") ||
	    STRING_HAS_PREFIX(key, "IO")) {
		hostArchKey = makeHostArchKey(key, &hostArchKeySize);
		if (!hostArchKey) {
			OSKextLog(/* kext (this isn't about a kext) */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
			    "Allocation failure.");
			goto finish;
		}
		result = infoDict->getObject(hostArchKey);
	}

	if (!result) {
		result = infoDict->getObject(key);
	}

finish:
	if (hostArchKey) {
		kfree_data(hostArchKey, hostArchKeySize);
	}
	return result;
}

#if PRAGMA_MARK
#pragma mark Load/Start/Stop/Unload
#endif

#define isWhiteSpace(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == ',' || (c) == '\n')

/*********************************************************************
* sExcludeListByID is a dictionary with keys / values of:
*  key = bundleID string of kext we will not allow to load
*  value = version string(s) of the kext that is to be denied loading.
*      The version strings can be comma delimited.  For example if kext
*      com.foocompany.fookext has two versions that we want to deny
*      loading then the version strings might look like:
*      1.0.0, 1.0.1
*      If the current fookext has a version of 1.0.0 OR 1.0.1 we will
*      not load the kext.
*
*      Value may also be in the form of "LE 2.0.0" (version numbers
*      less than or equal to 2.0.0 will not load) or "LT 2.0.0" (version
*      number less than 2.0.0 will not load)
*
*      NOTE - we cannot use the characters "<=" or "<" because we have code
*      that serializes plists and treats '<' as a special character.
*********************************************************************/
bool
OSKext::isInExcludeList(void)
{
	OSString *      versionString           = NULL;        // do not release
	char *          versionCString          = NULL;        // do not free
	size_t          i;
	boolean_t       wantLessThan = false;
	boolean_t       wantLessThanEqualTo = false;
	boolean_t       isInExcludeList = true;
	char            myBuffer[32];

	IORecursiveLockLock(sKextLock);

	if (!sExcludeListByID) {
		isInExcludeList = false;
	} else {
		/* look up by bundleID in our exclude list and if found get version
		 * string (or strings) that we will not allow to load
		 */
		versionString = OSDynamicCast(OSString, sExcludeListByID->getObject(bundleID.get()));
		if (versionString == NULL || versionString->getLength() > (sizeof(myBuffer) - 1)) {
			isInExcludeList = false;
		}
	}

	IORecursiveLockUnlock(sKextLock);

	if (!isInExcludeList) {
		return false;
	}

	/* parse version strings */
	versionCString = (char *) versionString->getCStringNoCopy();

	/* look for "LT" or "LE" form of version string, must be in first two
	 * positions.
	 */
	if (*versionCString == 'L' && *(versionCString + 1) == 'T') {
		wantLessThan = true;
		versionCString += 2;
	} else if (*versionCString == 'L' && *(versionCString + 1) == 'E') {
		wantLessThanEqualTo = true;
		versionCString += 2;
	}

	for (i = 0; *versionCString != 0x00; versionCString++) {
		/* skip whitespace */
		if (isWhiteSpace(*versionCString)) {
			continue;
		}

		/* peek ahead for version string separator or null terminator */
		if (*(versionCString + 1) == ',' || *(versionCString + 1) == 0x00) {
			/* OK, we have a version string */
			myBuffer[i++] = *versionCString;
			myBuffer[i] = 0x00;

			OSKextVersion excludeVers;
			excludeVers = OSKextParseVersionString(myBuffer);

			if (wantLessThanEqualTo) {
				if (version <= excludeVers) {
					return true;
				}
			} else if (wantLessThan) {
				if (version < excludeVers) {
					return true;
				}
			} else if (version == excludeVers) {
				return true;
			}

			/* reset for the next (if any) version string */
			i = 0;
			wantLessThan = false;
			wantLessThanEqualTo = false;
		} else {
			/* save valid version character */
			myBuffer[i++] = *versionCString;

			/* make sure bogus version string doesn't overrun local buffer */
			if (i >= sizeof(myBuffer)) {
				break;
			}
		}
	}

	return false;
}

/*********************************************************************
* sNonLoadableKextsByID is a dictionary with keys / values of:
*  key = bundleID string of kext we will not allow to load
*  value = boolean (true == loadable, false == not loadable)
*
*  Only kexts which are in the AuxKC will be marked as "not loadble,"
*  i.e., the value for the kext's bundleID will be false. All kexts in
*  the primary and system KCs will always be marked as "loadable."
*
*  This list ultimately comes from kexts which have been uninstalled
*  in user space by deleting the kext from disk, but which have not
*  yet been removed from the AuxKC. Because the user could choose to
*  re-install the exact same version of the kext, we need to keep
*  a dictionary of boolean values so that user space only needs to
*  keep a simple list of "uninstalled" or "missing" bundles. When
*  a bundle is re-installed, the iokit daemon can use the
*  AucKCBundleAvailable  predicate to set the individual kext's
*  availability to true.
*********************************************************************/
bool
OSKext::isLoadable(void)
{
	bool isLoadable = true;

	if (kc_type != KCKindAuxiliary) {
		/* this filtering only applies to kexts in the auxkc */
		return true;
	}

	IORecursiveLockLock(sKextLock);

	if (sNonLoadableKextsByID) {
		/* look up by bundleID in our exclude list and if found get version
		 * string (or strings) that we will not allow to load
		 */
		OSBoolean *loadableVal;
		loadableVal = OSDynamicCast(OSBoolean, sNonLoadableKextsByID->getObject(bundleID.get()));
		if (loadableVal && !loadableVal->getValue()) {
			isLoadable = false;
		}
	}
	IORecursiveLockUnlock(sKextLock);

	return isLoadable;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::loadKextWithIdentifier(
	const char       * kextIdentifierCString,
	Boolean            allowDeferFlag,
	Boolean            delayAutounloadFlag,
	OSKextExcludeLevel startOpt,
	OSKextExcludeLevel startMatchingOpt,
	OSArray          * personalityNames)
{
	OSReturn   result         = kOSReturnError;
	OSSharedPtr<OSString> kextIdentifier;

	kextIdentifier = OSString::withCString(kextIdentifierCString);
	if (!kextIdentifier) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}
	result = OSKext::loadKextWithIdentifier(kextIdentifier.get(),
	    NULL /* kextRef */,
	    allowDeferFlag, delayAutounloadFlag,
	    startOpt, startMatchingOpt, personalityNames);

finish:
	return result;
}

OSReturn
OSKext::loadKextWithIdentifier(
	OSString          * kextIdentifier,
	OSSharedPtr<OSObject>         &kextRef,
	Boolean             allowDeferFlag,
	Boolean             delayAutounloadFlag,
	OSKextExcludeLevel  startOpt,
	OSKextExcludeLevel  startMatchingOpt,
	OSArray           * personalityNames)
{
	OSObject * kextRefRaw = NULL;
	OSReturn result;

	result = loadKextWithIdentifier(kextIdentifier,
	    &kextRefRaw,
	    allowDeferFlag,
	    delayAutounloadFlag,
	    startOpt,
	    startMatchingOpt,
	    personalityNames);
	if ((kOSReturnSuccess == result) && kextRefRaw) {
		kextRef.reset(kextRefRaw, OSNoRetain);
	}
	return result;
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::loadKextWithIdentifier(
	OSString          * kextIdentifier,
	OSObject         ** kextRef,
	Boolean             allowDeferFlag,
	Boolean             delayAutounloadFlag,
	OSKextExcludeLevel  startOpt,
	OSKextExcludeLevel  startMatchingOpt,
	OSArray           * personalityNames)
{
	OSReturn          result               = kOSReturnError;
	OSReturn          pingResult           = kOSReturnError;
	OSKext          * theKext              = NULL;        // do not release
	OSSharedPtr<OSDictionary>   loadRequest;
	OSSharedPtr<const OSSymbol> kextIdentifierSymbol;

	if (kextRef) {
		*kextRef = NULL;
	}

	IORecursiveLockLock(sKextLock);

	if (!kextIdentifier) {
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	OSKext::recordIdentifierRequest(kextIdentifier);

	theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
	if (!theKext) {
		if (!allowDeferFlag) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Can't load kext %s - not found.",
			    kextIdentifier->getCStringNoCopy());
			goto finish;
		}

		if (!sKernelRequestsEnabled) {
			OSKextLog(theKext,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Can't load kext %s - requests to user space are disabled.",
			    kextIdentifier->getCStringNoCopy());
			result = kOSKextReturnDisabled;
			goto finish;
		}

		/* Create a new request unless one is already sitting
		 * in sKernelRequests for this bundle identifier
		 */
		kextIdentifierSymbol = OSSymbol::withString(kextIdentifier);
		if (!sPostedKextLoadIdentifiers->containsObject(kextIdentifierSymbol.get())) {
			result = _OSKextCreateRequest(kKextRequestPredicateRequestLoad,
			    loadRequest);
			if (result != kOSReturnSuccess) {
				goto finish;
			}
			if (!_OSKextSetRequestArgument(loadRequest.get(),
			    kKextRequestArgumentBundleIdentifierKey, kextIdentifier)) {
				result = kOSKextReturnNoMemory;
				goto finish;
			}
			if (!sKernelRequests->setObject(loadRequest.get())) {
				result = kOSKextReturnNoMemory;
				goto finish;
			}

			if (!sPostedKextLoadIdentifiers->setObject(kextIdentifierSymbol.get())) {
				result = kOSKextReturnNoMemory;
				goto finish;
			}

			OSKextLog(theKext,
			    kOSKextLogDebugLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s not found; queued load request to user space.",
			    kextIdentifier->getCStringNoCopy());
		}

		pingResult = OSKext::pingIOKitDaemon();
		if (pingResult == kOSKextReturnDisabled) {
			OSKextLog(/* kext */ NULL,
			    ((sPrelinkBoot) ? kOSKextLogDebugLevel : kOSKextLogErrorLevel) |
			    kOSKextLogLoadFlag,
			    "Kext %s might not load - " kIOKitDaemonName " is currently unavailable.",
			    kextIdentifier->getCStringNoCopy());
		}

		result = kOSKextReturnDeferred;
		goto finish;
	}

	result = theKext->load(startOpt, startMatchingOpt, personalityNames);

	if (result != kOSReturnSuccess) {
		OSKextLog(theKext,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Failed to load kext %s (error 0x%x).",
		    kextIdentifier->getCStringNoCopy(), (int)result);

		if (theKext->kc_type == KCKindUnknown) {
			OSKext::removeKext(theKext,
			    /* terminateService/removePersonalities */ true);
		}
		goto finish;
	}

	if (delayAutounloadFlag) {
		OSKextLog(theKext,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Setting delayed autounload for %s.",
		    kextIdentifier->getCStringNoCopy());
		theKext->flags.delayAutounload = 1;
	}

finish:
	if ((kOSReturnSuccess == result) && kextRef) {
		*kextRef = theKext;
		theKext->matchingRefCount++;
		theKext->retain();
	}

	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::loadKextFromKC(OSKext *theKext, OSDictionary *requestDict)
{
	OSReturn  result = kOSReturnError;

	OSBoolean *delayAutounloadBool     = NULL; // do not release
	OSNumber  *startKextExcludeNum     = NULL; // do not release
	OSNumber  *startMatchingExcludeNum = NULL; // do not release
	OSArray   *personalityNames        = NULL; // do not release

	/*
	 * Default values for these options:
	 *      regular autounload behavior
	 *      start the kext
	 *      send all personalities to the catalog
	 */
	Boolean            delayAutounload           = false;
	OSKextExcludeLevel startKextExcludeLevel     = kOSKextExcludeNone;
	OSKextExcludeLevel startMatchingExcludeLevel = kOSKextExcludeNone;

	IORecursiveLockLock(sKextLock);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogDebugLevel |
	    kOSKextLogIPCFlag,
	    "Received kext KC load request from user space.");

	/* Regardless of processing, the fact that we have gotten here means some
	 * user-space program is up and talking to us, so we'll switch our kext
	 * registration to reflect that.
	 */
	if (!sUserLoadsActive) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogProgressLevel |
		    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
		    "Switching to late startup (user-space) kext loading policy.");
		sUserLoadsActive = true;
	}

	delayAutounloadBool = OSDynamicCast(OSBoolean,
	    _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentDelayAutounloadKey));
	startKextExcludeNum = OSDynamicCast(OSNumber,
	    _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentStartExcludeKey));
	startMatchingExcludeNum = OSDynamicCast(OSNumber,
	    _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentStartMatchingExcludeKey));
	personalityNames = OSDynamicCast(OSArray,
	    _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentPersonalityNamesKey));

	if (delayAutounloadBool) {
		delayAutounload = delayAutounloadBool->getValue();
	}
	if (startKextExcludeNum) {
		startKextExcludeLevel = startKextExcludeNum->unsigned8BitValue();
	}
	if (startMatchingExcludeNum) {
		startMatchingExcludeLevel = startMatchingExcludeNum->unsigned8BitValue();
	}

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogIPCFlag,
	    "Received request from user space to load KC kext %s.",
	    theKext->getIdentifierCString());

	/* this could be in the Auxiliary KC, so record the load request */
	OSKext::recordIdentifierRequest(OSDynamicCast(OSString, theKext->getIdentifier()));

	/*
	 * Load the kext
	 */
	result = theKext->load(startKextExcludeLevel,
	    startMatchingExcludeLevel, personalityNames);

	if (result != kOSReturnSuccess) {
		OSKextLog(theKext,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Failed to load kext %s (error 0x%x).",
		    theKext->getIdentifierCString(), (int)result);

		OSKext::removeKext(theKext,
		    /* terminateService/removePersonalities */ true);
		goto finish;
	} else {
		OSKextLog(theKext,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s Loaded successfully from %s KC",
		    theKext->getIdentifierCString(), theKext->getKCTypeString());
	}

	if (delayAutounload) {
		OSKextLog(theKext,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Setting delayed autounload for %s.",
		    theKext->getIdentifierCString());
		theKext->flags.delayAutounload = 1;
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::loadCodelessKext(OSString *kextIdentifier, OSDictionary *requestDict)
{
	OSReturn  result = kOSReturnError;
	OSDictionary *anInfoDict = NULL; // do not release

	anInfoDict = OSDynamicCast(OSDictionary,
	    _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentCodelessInfoKey));
	if (anInfoDict == NULL) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
		    "Missing 'Codeless Kext Info' dictionary in codeless kext load request of %s.",
		    kextIdentifier->getCStringNoCopy());
		return kOSKextReturnInvalidArgument;
	}

	IORecursiveLockLock(sKextLock);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogIPCFlag,
	    "Received request from user space to load codeless kext %s.",
	    kextIdentifier->getCStringNoCopy());

	{
		// instantiate a new kext, and don't hold a reference
		// (the kext subsystem will hold one implicitly)
		OSKextInitResult ret;
		OSSharedPtr<OSKext> newKext = OSKext::withCodelessInfo(anInfoDict, &ret);
		if (!newKext) {
			/*
			 * We might have failed to create a new OSKext
			 * because the old one should still be used.
			 * Check if that is the case.
			 */
			if (ret != kOSKextInitFailure) {
				result = kOSReturnSuccess;
				goto finish;
			}
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
			    "Could not instantiate codeless kext.");
			result = kOSKextReturnNotLoadable;
			goto finish;
		}
		if (!kextIdentifier->isEqualTo(newKext->getIdentifierCString())) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag | kOSKextLogLoadFlag,
			    "Codeless kext identifiers don't match '%s' != '%s'",
			    kextIdentifier->getCStringNoCopy(), newKext->getIdentifierCString());

			OSKext::removeKext(newKext.get(), false);
			result = kOSKextReturnInvalidArgument;
			goto finish;
		}

		/* Record the request for the codeless kext */
		OSKext::recordIdentifierRequest(OSDynamicCast(OSString, newKext->getIdentifier()));

		result = kOSReturnSuccess;
		/* Send the kext's personalities to the IOCatalog. This is an explicit load. */
		result = newKext->sendPersonalitiesToCatalog(true, NULL);
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::dropMatchingReferences(
	OSSet * kexts)
{
	IORecursiveLockLock(sKextLock);
	kexts->iterateObjects(^bool (OSObject * obj) {
		OSKext * thisKext = OSDynamicCast(OSKext, obj);
		if (!thisKext) {
		        return false;
		}
		thisKext->matchingRefCount--;
		return false;
	});
	IORecursiveLockUnlock(sKextLock);
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::recordIdentifierRequest(
	OSString * kextIdentifier)
{
	OSSharedPtr<const OSSymbol> kextIdentifierSymbol;
	bool             fail                 = false;

	if (!sAllKextLoadIdentifiers || !kextIdentifier) {
		goto finish;
	}

	kextIdentifierSymbol = OSSymbol::withString(kextIdentifier);
	if (!kextIdentifierSymbol) {
		// xxx - this is really a basic alloc failure
		fail = true;
		goto finish;
	}

	IORecursiveLockLock(sKextLock);
	if (!sAllKextLoadIdentifiers->containsObject(kextIdentifierSymbol.get())) {
		if (!sAllKextLoadIdentifiers->setObject(kextIdentifierSymbol.get())) {
			fail = true;
		} else {
			// xxx - need to find a way to associate this whole func w/the kext
			OSKextLog(/* kext */ NULL,
			    // xxx - check level
			    kOSKextLogStepLevel |
			    kOSKextLogArchiveFlag,
			    "Recorded kext %s as a candidate for inclusion in prelinked kernel.",
			    kextIdentifier->getCStringNoCopy());
		}
	}
	IORecursiveLockUnlock(sKextLock);

finish:

	if (fail) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogArchiveFlag,
		    "Failed to record kext %s as a candidate for inclusion in prelinked kernel.",
		    kextIdentifier->getCStringNoCopy());
	}
	return;
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::load(
	OSKextExcludeLevel   startOpt,
	OSKextExcludeLevel   startMatchingOpt,
	OSArray            * personalityNames)
{
	OSReturn             result                       = kOSReturnError;
	OSKextExcludeLevel   dependenciesStartOpt         = startOpt;
	OSKextExcludeLevel   dependenciesStartMatchingOpt = startMatchingOpt;
	unsigned int         i, count;
	Boolean              alreadyLoaded                = false;
	OSKext             * lastLoadedKext               = NULL;        // do not release

	if (isInExcludeList()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel | kOSKextLogGeneralFlag |
		    kOSKextLogLoadFlag,
		    "Kext %s is in exclude list, not loadable",
		    getIdentifierCString());

		result = kOSKextReturnNotLoadable;
		goto finish;
	}
	if (!isLoadable()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel | kOSKextLogGeneralFlag |
		    kOSKextLogLoadFlag,
		    "Kext %s is not loadable",
		    getIdentifierCString());

		result = kOSKextReturnNotLoadable;
		goto finish;
	}

	if (isLoaded()) {
		alreadyLoaded = true;
		result = kOSReturnSuccess;

		OSKextLog(this,
		    kOSKextLogDebugLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Kext %s is already loaded.",
		    getIdentifierCString());
		goto loaded;
	}

#if CONFIG_MACF
	/*
	 * On kxld and on embedded, only call into the MAC hook when on a
	 * user thread, for access control over userspace kextloads.
	 *
	 * On non-kxld systems, additionally check the MAC hook for kexts in
	 * the Pageable and Aux KCs, regardless of whether we are on a user
	 * thread or not. This means on Apple silicon devices that the MAC
	 * hook will only be useful to block 3rd party kexts loaded via
	 * matching, and any kexts loaded from userspace kextloads.
	 *
	 * Note that this should _not_ be called on kexts loaded from the
	 * kernel bootstrap thread as the kernel proc's cred struct is not
	 * yet initialized! This won't happen on macOS because all the kexts
	 * in the BootKC are self-contained and their kc_type = KCKindPrimary.
	 */
	if (current_task() != kernel_task
#if XNU_TARGET_OS_OSX && !CONFIG_KXLD
	    || (kc_type != KCKindPrimary && kc_type != KCKindUnknown)
#endif
	    ) {
		int                 macCheckResult      = 0;
		kauth_cred_t        cred                = NULL;

		cred = kauth_cred_get_with_ref();
		macCheckResult = mac_kext_check_load(cred, getIdentifierCString());
		kauth_cred_unref(&cred);

		if (macCheckResult != 0) {
			result = kOSReturnError;
			OSKextLog(this,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Failed to load kext %s (MAC policy error 0x%x).",
			    getIdentifierCString(), macCheckResult);
			goto finish;
		}
	}
#endif /* CONFIG_MACF */

	if (!sLoadEnabled) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext loading is disabled (attempt to load kext %s).",
		    getIdentifierCString());
		result = kOSKextReturnDisabled;
		goto finish;
	}

	/* If we've pushed the next available load tag to the invalid value,
	 * we can't load any more kexts.
	 */
	if (sNextLoadTag == kOSKextInvalidLoadTag) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Can't load kext %s - no more load tags to assign.",
		    getIdentifierCString());
		result = kOSKextReturnNoResources;
		goto finish;
	}

	/* This is a bit of a hack, because we shouldn't be handling
	 * personalities within the load function.
	 */
	if (!declaresExecutable()) {
		/* There is a special case where a non-executable kext can be loaded: the
		 * AppleKextExcludeList.  Detect that special kext by bundle identifier and
		 * load its metadata into the global data structures, if appropriate
		 */
		if (strcmp(getIdentifierCString(), kIOExcludeListBundleID) == 0) {
			boolean_t updated = updateExcludeList(infoDict.get());
			if (updated) {
				OSKextLog(this,
				    kOSKextLogDebugLevel | kOSKextLogLoadFlag,
				    "KextExcludeList was updated to version: %lld", sExcludeListVersion);
			}
		}

		if (isDriverKit()) {
			if (loadTag == 0) {
				sLoadedDriverKitKexts->setObject(this);
				loadTag = sNextLoadTag++;
			}
		}
		result = kOSReturnSuccess;
		goto loaded;
	}

	/* Are we in safe boot?
	 */
	if (sSafeBoot && !isLoadableInSafeBoot()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Can't load kext %s - not loadable during safe boot.",
		    getIdentifierCString());
		result = kOSKextReturnBootLevel;
		goto finish;
	}

	OSKextLog(this,
	    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
	    "Loading kext %s.",
	    getIdentifierCString());

#if !VM_MAPPED_KEXTS
	if (isPrelinked() == false) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Can't load kext %s - not in a kext collection.",
		    getIdentifierCString());
		result = kOSKextReturnDisabled;
		goto finish;
	}
#endif /* defined(__x86_64__) */

#if CONFIG_KXLD
	if (!sKxldContext) {
		kern_return_t kxldResult;
		kxldResult = kxld_create_context(&sKxldContext, &kern_allocate,
		    &kxld_log_callback, /* Flags */ (KXLDFlags) 0,
		    /* cputype */ 0, /* cpusubtype */ 0, /* page size */ 0);
		if (kxldResult) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag | kOSKextLogLinkFlag,
			    "Can't load kext %s - failed to create link context.",
			    getIdentifierCString());
			result = kOSKextReturnNoMemory;
			goto finish;
		}
	}
#endif // CONFIG_KXLD

	/* We only need to resolve dependencies once for the whole graph, but
	 * resolveDependencies will just return if there's no work to do, so it's
	 * safe to call it more than once.
	 */
	if (!resolveDependencies()) {
		// xxx - check resolveDependencies() for log msg
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
		    "Can't load kext %s - failed to resolve library dependencies.",
		    getIdentifierCString());
		result = kOSKextReturnDependencies;
		goto finish;
	}

	/* If we are excluding just the kext being loaded now (and not its
	 * dependencies), drop the exclusion level to none so dependencies
	 * start and/or add their personalities.
	 */
	if (dependenciesStartOpt == kOSKextExcludeKext) {
		dependenciesStartOpt = kOSKextExcludeNone;
	}

	if (dependenciesStartMatchingOpt == kOSKextExcludeKext) {
		dependenciesStartMatchingOpt = kOSKextExcludeNone;
	}

	/* Load the dependencies, recursively.
	 */
	count = getNumDependencies();
	for (i = 0; i < count; i++) {
		OSKext * dependency = OSDynamicCast(OSKext,
		    dependencies->getObject(i));
		if (dependency == NULL) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
			    "Internal error loading kext %s; dependency disappeared.",
			    getIdentifierCString());
			result = kOSKextReturnInternalError;
			goto finish;
		}

		/* Dependencies must be started accorting to the opt,
		 * but not given the personality names of the main kext.
		 */
		result = dependency->load(dependenciesStartOpt,
		    dependenciesStartMatchingOpt,
		    /* personalityNames */ NULL);
		if (result != KERN_SUCCESS) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
			    "Dependency %s of kext %s failed to load.",
			    dependency->getIdentifierCString(),
			    getIdentifierCString());

			OSKext::removeKext(dependency,
			    /* terminateService/removePersonalities */ true);
			result = kOSKextReturnDependencyLoadError;

			goto finish;
		}
	}

	result = loadExecutable();
	if (result != KERN_SUCCESS) {
		goto finish;
	}

	pendingPgoHead.next = &pendingPgoHead;
	pendingPgoHead.prev = &pendingPgoHead;

	// The kernel PRNG is not initialized when the first kext is
	// loaded, so use early random
	uuid_generate_early_random(instance_uuid);
	account = IOMallocType(OSKextAccount);

	account->loadTag = kmod_info->id;
	account->site.refcount = 0;
	account->site.flags = VM_TAG_KMOD;

#if DEVELOPMENT || DEBUG
	/* Setup the task reference group. */
	(void)snprintf(account->task_refgrp_name, sizeof(account->task_refgrp_name),
	    "task_%s", getIdentifierCString());
	account->task_refgrp.grp_name = account->task_refgrp_name;
	account->task_refgrp.grp_parent = &task_external_refgrp;
	account->task_refgrp.grp_flags = OS_REFGRP_F_ALWAYS_ENABLED;
	os_ref_log_init(&account->task_refgrp);
#endif /* DEVELOPMENT || DEBUG */

	account->kext = this;
	if (gIOSurfaceIdentifier == bundleID) {
		vm_tag_alloc(&account->site);
		gIOSurfaceTag = account->site.tag;
	}

	flags.loaded = true;

	/* Add the kext to the list of loaded kexts and update the kmod_info
	 * struct to point to that of the last loaded kext (which is the way
	 * it's always been done, though I'd rather do them in order now).
	 */
	lastLoadedKext = OSDynamicCast(OSKext, sLoadedKexts->getLastObject());
	sLoadedKexts->setObject(this);

	/* Keep the kernel itself out of the kmod list.
	 */
	if (lastLoadedKext->isKernel()) {
		lastLoadedKext = NULL;
	}

	if (lastLoadedKext) {
		kmod_info->next = lastLoadedKext->kmod_info;
	}

	notifyKextLoadObservers(this, kmod_info);

	/* Make the global kmod list point at the just-loaded kext. Note that the
	 * __kernel__ kext isn't in this list, as it wasn't before SnowLeopard,
	 * although we do report it in kextstat these days by using the newer
	 * OSArray of loaded kexts, which does contain it.
	 *
	 * (The OSKext object representing the kernel doesn't even have a kmod_info
	 * struct, though I suppose we could stick a pointer to it from the
	 * static struct in OSRuntime.cpp.)
	 */
	kmod = kmod_info;

	/* Save the list of loaded kexts in case we panic.
	 */
	OSKext::saveLoadedKextPanicList();

	if (isExecutable()) {
		OSKext::updateLoadedKextSummaries();
		savePanicString(/* isLoading */ true);

#if CONFIG_DTRACE
		registerWithDTrace();
#else
		jettisonLinkeditSegment();
#endif /* CONFIG_DTRACE */

#if !VM_MAPPED_KEXTS
		/* If there is a page (or more) worth of padding after the end
		 * of the last data section but before the end of the data segment
		 * then free it in the same manner the LinkeditSegment is freed
		 */
		jettisonDATASegmentPadding();
#endif
	}

loaded:
	if (isExecutable() && !flags.started) {
		if (startOpt == kOSKextExcludeNone) {
			result = start();
			if (result != kOSReturnSuccess) {
				OSKextLog(this,
				    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
				    "Kext %s start failed (result 0x%x).",
				    getIdentifierCString(), result);
				result = kOSKextReturnStartStopError;
			}
		}
	}

	/* If not excluding matching, send the personalities to the kernel.
	 * This never affects the result of the load operation.
	 * This is a bit of a hack, because we shouldn't be handling
	 * personalities within the load function.
	 */
	if (result == kOSReturnSuccess && startMatchingOpt == kOSKextExcludeNone) {
		result = sendPersonalitiesToCatalog(true, personalityNames);
	}

finish:

	if (result != kOSReturnSuccess) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s failed to load (0x%x).",
		    getIdentifierCString(), (int)result);
	} else if (!alreadyLoaded) {
		OSKextLog(this,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s loaded.",
		    getIdentifierCString());

		queueKextNotification(kKextRequestPredicateLoadNotification,
		    OSDynamicCast(OSString, bundleID.get()), getDextUniqueID());
	}
	return result;
}

#if CONFIG_KXLD
/*********************************************************************
*
*********************************************************************/
static char *
strdup(const char * string)
{
	char * result = NULL;
	size_t size;

	if (!string) {
		goto finish;
	}

	size = 1 + strlen(string);
	result = (char *)kalloc_data_tag(size, Z_WAITOK, VM_KERN_MEMORY_OSKEXT);
	if (!result) {
		goto finish;
	}

	memcpy(result, string, size);

finish:
	return result;
}
#endif // CONFIG_KXLD

/*********************************************************************
*
*********************************************************************/

kernel_section_t *
OSKext::lookupSection(const char *segname, const char *secname)
{
	kernel_section_t         * found_section = NULL;
	kernel_mach_header_t     * mh            = NULL;
	kernel_segment_command_t * seg           = NULL;
	kernel_section_t         * sec           = NULL;

	if (!linkedExecutable) {
		return NULL;
	}

	mh = (kernel_mach_header_t *)linkedExecutable->getBytesNoCopy();

	for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
		if (0 != strncmp(seg->segname, segname, sizeof(seg->segname))) {
			continue;
		}

		for (sec = firstsect(seg); sec != NULL; sec = nextsect(seg, sec)) {
			if (0 == strncmp(sec->sectname, secname, sizeof(sec->sectname))) {
				found_section = sec;
				goto out;
			}
		}
	}

out:
	return found_section;
}

/*********************************************************************
*
*********************************************************************/

OSReturn
OSKext::slidePrelinkedExecutable(bool doCoalescedSlides)
{
	OSReturn                       result           = kOSKextReturnBadData;
	kernel_mach_header_t         * mh               = NULL;
	kernel_segment_command_t     * seg              = NULL;
	kernel_segment_command_t     * linkeditSeg      = NULL;
	kernel_section_t             * sec              = NULL;
	char                         * linkeditBase     = NULL;
	bool                           haveLinkeditBase = false;
	char                         * relocBase        = NULL;
	bool                           haveRelocBase    = false;
	struct dysymtab_command      * dysymtab         = NULL;
	struct linkedit_data_command * segmentSplitInfo = NULL;
	struct symtab_command        * symtab           = NULL;
	kernel_nlist_t               * sym              = NULL;
	struct relocation_info       * reloc            = NULL;
	uint32_t                       i                = 0;
	int                            reloc_size;
	vm_offset_t                    new_kextsize;
	kc_format                    format             = KCFormatUnknown;

	if (linkedExecutable == NULL || flags.builtin) {
		result = kOSReturnSuccess;
		goto finish;
	}

	mh = (kernel_mach_header_t *)linkedExecutable->getBytesNoCopy();
	if (kernel_mach_header_is_in_fileset(mh)) {
		// kexts in filesets are slid as part of collection sliding
		result = kOSReturnSuccess;
		goto finish;
	}

	segmentSplitInfo = (struct linkedit_data_command *) getcommandfromheader(mh, LC_SEGMENT_SPLIT_INFO);

	for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
		if (!seg->vmaddr) {
			continue;
		}

		seg->vmaddr = ml_static_slide(seg->vmaddr);

#if KASLR_KEXT_DEBUG
		IOLog("kaslr: segname %s unslid 0x%lx slid 0x%lx \n",
		    seg->segname,
		    (unsigned long)ml_static_unslide(seg->vmaddr),
		    (unsigned long)seg->vmaddr);
#endif

		if (!haveRelocBase) {
			relocBase = (char *) seg->vmaddr;
			haveRelocBase = true;
		}
		if (!strcmp(seg->segname, "__LINKEDIT")) {
			linkeditBase = (char *) seg->vmaddr - seg->fileoff;
			haveLinkeditBase = true;
			linkeditSeg = seg;
		}
		for (sec = firstsect(seg); sec != NULL; sec = nextsect(seg, sec)) {
			sec->addr = ml_static_slide(sec->addr);

#if KASLR_KEXT_DEBUG
			IOLog("kaslr: sectname %s unslid 0x%lx slid 0x%lx \n",
			    sec->sectname,
			    (unsigned long)ml_static_unslide(sec->addr),
			    (unsigned long)sec->addr);
#endif
		}
	}

	dysymtab = (struct dysymtab_command *) getcommandfromheader(mh, LC_DYSYMTAB);

	symtab = (struct symtab_command *) getcommandfromheader(mh, LC_SYMTAB);

	if (symtab != NULL && doCoalescedSlides == false) {
		/* Some pseudo-kexts have symbol tables without segments.
		 * Ignore them. */
		if (symtab->nsyms > 0 && haveLinkeditBase) {
			sym = (kernel_nlist_t *) (linkeditBase + symtab->symoff);
			for (i = 0; i < symtab->nsyms; i++) {
				if (sym[i].n_type & N_STAB) {
					continue;
				}
				sym[i].n_value = ml_static_slide(sym[i].n_value);

#if KASLR_KEXT_DEBUG
#define MAX_SYMS_TO_LOG 5
				if (i < MAX_SYMS_TO_LOG) {
					IOLog("kaslr: LC_SYMTAB unslid 0x%lx slid 0x%lx \n",
					    (unsigned long)ml_static_unslide(sym[i].n_value),
					    (unsigned long)sym[i].n_value);
				}
#endif
			}
		}
	}

	if (dysymtab != NULL && doCoalescedSlides == false) {
		if (dysymtab->nextrel > 0) {
			OSKextLog(this,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag |
			    kOSKextLogLinkFlag,
			    "Sliding kext %s: External relocations found.",
			    getIdentifierCString());
			goto finish;
		}

		if (dysymtab->nlocrel > 0) {
			if (!haveLinkeditBase) {
				OSKextLog(this,
				    kOSKextLogErrorLevel | kOSKextLogLoadFlag |
				    kOSKextLogLinkFlag,
				    "Sliding kext %s: No linkedit segment.",
				    getIdentifierCString());
				goto finish;
			}

			if (!haveRelocBase) {
				OSKextLog(this,
				    kOSKextLogErrorLevel | kOSKextLogLoadFlag |
				    kOSKextLogLinkFlag,
#if __x86_64__
				    "Sliding kext %s: No writable segments.",
#else
				    "Sliding kext %s: No segments.",
#endif
				    getIdentifierCString());
				goto finish;
			}

			reloc = (struct relocation_info *) (linkeditBase + dysymtab->locreloff);
			reloc_size = dysymtab->nlocrel * sizeof(struct relocation_info);

			for (i = 0; i < dysymtab->nlocrel; i++) {
				if (reloc[i].r_extern != 0
				    || reloc[i].r_type != 0
				    || reloc[i].r_length != (sizeof(void *) == 8 ? 3 : 2)
				    ) {
					OSKextLog(this,
					    kOSKextLogErrorLevel | kOSKextLogLoadFlag |
					    kOSKextLogLinkFlag,
					    "Sliding kext %s: Unexpected relocation found.",
					    getIdentifierCString());
					goto finish;
				}
				if (reloc[i].r_pcrel != 0) {
					continue;
				}
				uintptr_t *relocAddr = (uintptr_t*)(relocBase + reloc[i].r_address);
				*relocAddr = ml_static_slide(*relocAddr);

#if KASLR_KEXT_DEBUG
#define MAX_DYSYMS_TO_LOG 5
				if (i < MAX_DYSYMS_TO_LOG) {
					IOLog("kaslr: LC_DYSYMTAB unslid 0x%lx slid 0x%lx \n",
					    (unsigned long)ml_static_unslide(*((uintptr_t *)(relocAddr))),
					    (unsigned long)*((uintptr_t *)(relocBase + reloc[i].r_address)));
				}
#endif
			}

			/* We should free these relocations, not just delete the reference to them.
			 * <rdar://problem/10535549> Free relocations from PIE kexts.
			 *
			 * For now, we do not free LINKEDIT for kexts with split segments.
			 */
			new_kextsize = round_page(kmod_info->size - reloc_size);
			if (new_kextsize > UINT_MAX) {
				OSKextLog(this,
				    kOSKextLogErrorLevel | kOSKextLogLoadFlag |
				    kOSKextLogLinkFlag,
				    "Kext %s: new kext size is too large.",
				    getIdentifierCString());
				goto finish;
			}
			if (((kmod_info->size - new_kextsize) > PAGE_SIZE) && (!segmentSplitInfo)) {
				vm_offset_t     endofkext = kmod_info->address + kmod_info->size;
				vm_offset_t     new_endofkext = kmod_info->address + new_kextsize;
				vm_offset_t     endofrelocInfo = (vm_offset_t) (((uint8_t *)reloc) + reloc_size);
				size_t          bytes_remaining = endofkext - endofrelocInfo;
				OSSharedPtr<OSData>        new_osdata;

				/* fix up symbol offsets if they are after the dsymtab local relocs */
				if (symtab) {
					if (dysymtab->locreloff < symtab->symoff) {
						symtab->symoff -= reloc_size;
					}
					if (dysymtab->locreloff < symtab->stroff) {
						symtab->stroff -= reloc_size;
					}
				}
				if (dysymtab->locreloff < dysymtab->extreloff) {
					dysymtab->extreloff -= reloc_size;
				}

				/* move data behind reloc info down to new offset */
				if (endofrelocInfo < endofkext) {
					memcpy(reloc, (void *)endofrelocInfo, bytes_remaining);
				}

				/* Create a new OSData for the smaller kext object and reflect
				 * new linkedit segment size.
				 */
				linkeditSeg->vmsize = round_page(linkeditSeg->vmsize - reloc_size);
				linkeditSeg->filesize = linkeditSeg->vmsize;

				new_osdata = OSData::withBytesNoCopy((void *)kmod_info->address, (unsigned int)new_kextsize);
				if (new_osdata) {
					/* Fix up kmod info and linkedExecutable.
					 */
					kmod_info->size = new_kextsize;
					/*
					 * Fileset KCs are mapped as a whole by iBoot.
					 * Individual kext executables should not be unmapped
					 * by xnu.
					 * Doing so may result in panics like rdar://85419651
					 */
					if (PE_get_kc_format(kc_type, &format) && (format == KCFormatFileset)) {
						new_osdata->setDeallocFunction(NULL);
					} else { // Not from a Fileset KC
#if VM_MAPPED_KEXTS
						new_osdata->setDeallocFunction(osdata_kext_free);
#else
						new_osdata->setDeallocFunction(osdata_phys_free);
#endif
					}
					linkedExecutable->setDeallocFunction(NULL);
					linkedExecutable = os::move(new_osdata);

#if VM_MAPPED_KEXTS
					kext_free(new_endofkext, (endofkext - new_endofkext));
#else
					ml_static_mfree(new_endofkext, (endofkext - new_endofkext));
#endif
				}
			}
			dysymtab->nlocrel = 0;
			dysymtab->locreloff = 0;
		}
	}

	result = kOSReturnSuccess;
finish:
	return result;
}

/*********************************************************************
* called only by load()
*********************************************************************/
OSReturn
OSKext::loadExecutable()
{
	OSReturn              result             = kOSReturnError;
	OSSharedPtr<OSArray>  linkDependencies;
	uint32_t              num_kmod_refs      = 0;
	OSData              * theExecutable      = NULL;        // do not release
	OSString            * versString         = NULL;        // do not release
	const char          * versCString        = NULL;        // do not free
	const char          * string             = NULL;        // do not free

#if CONFIG_KXLD
	unsigned int          i;
	uint32_t              numDirectDependencies   = 0;
	kern_return_t         kxldResult;
	KXLDDependency     *  kxlddeps           = NULL;        // must kfree
	uint32_t              num_kxlddeps       = 0;
	struct mach_header ** kxldHeaderPtr      = NULL;        // do not free
	struct mach_header  * kxld_header        = NULL;        // xxx - need to free here?
#endif // CONFIG_KXLD

	/* We need the version string for a variety of bits below.
	 */
	versString = OSDynamicCast(OSString,
	    getPropertyForHostArch(kCFBundleVersionKey));
	if (!versString) {
		goto finish;
	}
	versCString = versString->getCStringNoCopy();

	if (isKernelComponent()) {
		if (STRING_HAS_PREFIX(versCString, KERNEL_LIB_PREFIX)) {
			if (strncmp(versCString, KERNEL6_VERSION, strlen(KERNEL6_VERSION))) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Kernel component %s has incorrect version %s; "
				    "expected %s.",
				    getIdentifierCString(),
				    versCString, KERNEL6_VERSION);
				result = kOSKextReturnInternalError;
				goto finish;
			} else if (strcmp(versCString, osrelease)) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Kernel component %s has incorrect version %s; "
				    "expected %s.",
				    getIdentifierCString(),
				    versCString, osrelease);
				result = kOSKextReturnInternalError;
				goto finish;
			}
		}
	}

#if defined(__x86_64__) || defined(__i386__)
	if (flags.resetSegmentsFromVnode) {
		/* Fixup the chains and slide the mach headers */
		kernel_mach_header_t *mh = (kernel_mach_header_t *)kmod_info->address;

		if (i386_slide_individual_kext(mh, PE_get_kc_slide(kc_type)) != KERN_SUCCESS) {
			result = kOSKextReturnValidation;
			goto finish;
		}
	}
#endif //(__x86_64__) || defined(__i386__)

	if (isPrelinked()) {
		goto register_kmod;
	}

	/* <rdar://problem/21444003> all callers must be entitled */
	if (FALSE == IOCurrentTaskHasEntitlement(kOSKextCollectionManagementEntitlement)) {
		OSKextLog(this,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Not entitled to link kext '%s'",
		    getIdentifierCString());
		result = kOSKextReturnNotPrivileged;
		goto finish;
	}

	theExecutable = getExecutable();
	if (!theExecutable) {
		if (declaresExecutable()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Can't load kext %s - executable is missing.",
			    getIdentifierCString());
			result = kOSKextReturnValidation;
			goto finish;
		}
		goto register_kmod;
	}

	if (isInterface()) {
		OSSharedPtr<OSData> executableCopy = OSData::withData(theExecutable);
		if (executableCopy) {
			setLinkedExecutable(executableCopy.get());
		}
		goto register_kmod;
	}

#if CONFIG_KXLD
	numDirectDependencies = getNumDependencies();

	if (flags.hasBleedthrough) {
		linkDependencies = dependencies;
	} else {
		linkDependencies = OSArray::withArray(dependencies.get());
		if (!linkDependencies) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag | kOSKextLogLinkFlag,
			    "Can't allocate link dependencies to load kext %s.",
			    getIdentifierCString());
			goto finish;
		}

		for (i = 0; i < numDirectDependencies; ++i) {
			OSKext * dependencyKext = OSDynamicCast(OSKext,
			    dependencies->getObject(i));
			dependencyKext->addBleedthroughDependencies(linkDependencies.get());
		}
	}

	num_kxlddeps = linkDependencies->getCount();
	if (!num_kxlddeps) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogDependenciesFlag,
		    "Can't load kext %s - it has no library dependencies.",
		    getIdentifierCString());
		goto finish;
	}

	kxlddeps = kalloc_type_tag(KXLDDependency, num_kxlddeps, Z_WAITOK, VM_KERN_MEMORY_OSKEXT);
	if (!kxlddeps) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogLinkFlag,
		    "Can't allocate link context to load kext %s.",
		    getIdentifierCString());
		goto finish;
	}
	bzero(kxlddeps, num_kxlddeps * sizeof(*kxlddeps));

	for (i = 0; i < num_kxlddeps; ++i) {
		OSKext * dependency = OSDynamicCast(OSKext, linkDependencies->getObject(i));

		if (dependency->isInterface()) {
			OSKext *interfaceTargetKext = NULL;        //do not release
			OSData * interfaceTarget = NULL;        //do not release

			if (dependency->isKernelComponent()) {
				interfaceTargetKext = sKernelKext;
				interfaceTarget = sKernelKext->linkedExecutable.get();
			} else {
				interfaceTargetKext = OSDynamicCast(OSKext,
				    dependency->dependencies->getObject(0));

				interfaceTarget = interfaceTargetKext->linkedExecutable.get();
			}

			if (!interfaceTarget) {
				// panic?
				goto finish;
			}

			/* The names set here aren't actually logged yet <rdar://problem/7941514>,
			 * it will be useful to have them in the debugger.
			 * strdup() failing isn't critical right here so we don't check that.
			 */
			kxlddeps[i].kext = (u_char *) interfaceTarget->getBytesNoCopy();
			kxlddeps[i].kext_size = interfaceTarget->getLength();
			kxlddeps[i].kext_name = strdup(interfaceTargetKext->getIdentifierCString());

			if (dependency->linkedExecutable != NULL) {
				kxlddeps[i].interface = (u_char *) dependency->linkedExecutable->getBytesNoCopy();
				kxlddeps[i].interface_size = dependency->linkedExecutable->getLength();
			} else {
				kxlddeps[i].interface = (u_char *) NULL;
				kxlddeps[i].interface_size = 0;
			}
			kxlddeps[i].interface_name = strdup(dependency->getIdentifierCString());
		} else {
			kxlddeps[i].kext = (u_char *) dependency->linkedExecutable->getBytesNoCopy();
			kxlddeps[i].kext_size = dependency->linkedExecutable->getLength();
			kxlddeps[i].kext_name = strdup(dependency->getIdentifierCString());
		}

		kxlddeps[i].is_direct_dependency = (i < numDirectDependencies);
	}

	kxldHeaderPtr = &kxld_header;

#if DEBUG
	OSKextLog(this,
	    kOSKextLogExplicitLevel |
	    kOSKextLogLoadFlag | kOSKextLogLinkFlag,
	    "Kext %s - calling kxld_link_file:\n"
	    "    kxld_context: %p\n"
	    "    executable: %p    executable_length: %d\n"
	    "    user_data: %p\n"
	    "    kxld_dependencies: %p    num_dependencies: %d\n"
	    "    kxld_header_ptr: %p    kmod_info_ptr: %p\n",
	    getIdentifierCString(), sKxldContext,
	    theExecutable->getBytesNoCopy(), theExecutable->getLength(),
	    this, kxlddeps, num_kxlddeps,
	    kxldHeaderPtr, &kmod_info);
#endif

	/* After this call, the linkedExecutable instance variable
	 * should exist.
	 */
	kxldResult = kxld_link_file(sKxldContext,
	    (u_char *)theExecutable->getBytesNoCopy(),
	    theExecutable->getLength(),
	    getIdentifierCString(), this, kxlddeps, num_kxlddeps,
	    (u_char **)kxldHeaderPtr, (kxld_addr_t *)&kmod_info);

	if (kxldResult != KERN_SUCCESS) {
		// xxx - add kxldResult here?
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Can't load kext %s - link failed.",
		    getIdentifierCString());
		result = kOSKextReturnLinkError;
		goto finish;
	}

	/* We've written data & instructions into kernel memory, so flush the data
	 * cache and invalidate the instruction cache.
	 * I/D caches are coherent on x86
	 */
#if !defined(__i386__) && !defined(__x86_64__)
	flush_dcache(kmod_info->address, kmod_info->size, false);
	invalidate_icache(kmod_info->address, kmod_info->size, false);
#endif

#else // !CONFIG_KXLD
	OSKextLog(this, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
	    "Refusing to link non-prelinked kext: %s (no kxld support)", getIdentifierCString());
	result = kOSKextReturnLinkError;
	goto finish;
#endif // CONFIG_KXLD

register_kmod:

	if (isInterface()) {
		/* Whip up a fake kmod_info entry for the interface kext.
		 */
		kmod_info = kalloc_type(kmod_info_t, (zalloc_flags_t)(Z_WAITOK | Z_ZERO));
		if (!kmod_info) {
			result = KERN_MEMORY_ERROR;
			goto finish;
		}

		/* A pseudokext has almost nothing in its kmod_info struct.
		 */
		kmod_info->info_version = KMOD_INFO_VERSION;

		/* An interface kext doesn't have a linkedExecutable, so save a
		 * copy of the UUID out of the original executable via copyUUID()
		 * while we still have the original executable.
		 */
		interfaceUUID = copyUUID();
	}

	kmod_info->id = loadTag = sNextLoadTag++;
	kmod_info->reference_count = 0;         // KMOD_DECL... sets it to -1 (invalid).

	/* Stamp the bundle ID and version from the OSKext over anything
	 * resident inside the kmod_info.
	 */
	string = getIdentifierCString();
	strlcpy(kmod_info->name, string, sizeof(kmod_info->name));

	string = versCString;
	strlcpy(kmod_info->version, string, sizeof(kmod_info->version));

	/* Add the dependencies' kmod_info structs as kmod_references.
	 */
	num_kmod_refs = getNumDependencies();
	if (num_kmod_refs) {
		kmod_info->reference_list = kalloc_type_tag(kmod_reference_t,
		    num_kmod_refs, Z_WAITOK_ZERO, VM_KERN_MEMORY_OSKEXT);
		if (!kmod_info->reference_list) {
			result = KERN_MEMORY_ERROR;
			goto finish;
		}
		for (uint32_t refIndex = 0; refIndex < num_kmod_refs; refIndex++) {
			kmod_reference_t * ref = &(kmod_info->reference_list[refIndex]);
			OSKext * refKext = OSDynamicCast(OSKext, dependencies->getObject(refIndex));
			ref->info = refKext->kmod_info;
			ref->info->reference_count++;

			if (refIndex + 1 < num_kmod_refs) {
				ref->next = kmod_info->reference_list + refIndex + 1;
			}
		}
	}

	if (kmod_info->hdr_size > UINT32_MAX) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
#if __LP64__
		    "Kext %s header size is too large (%lu > UINT32_MAX).",
#else
		    "Kext %s header size is too large (%u > UINT32_MAX).",
#endif
		    kmod_info->name,
		    kmod_info->hdr_size);
		result = KERN_FAILURE;
		goto finish;
	}

	if (kmod_info->size > UINT32_MAX) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
#if __LP64__
		    "Kext %s size is too large (%lu > UINT32_MAX).",
#else
		    "Kext %s size is too large (%u > UINT32_MAX).",
#endif
		    kmod_info->name,
		    kmod_info->size);
		result = KERN_FAILURE;
		goto finish;
	}

	if (!isInterface() && linkedExecutable) {
		OSKextLog(this,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s executable loaded; %u pages at 0x%lx (load tag %u).",
		    kmod_info->name,
		    (unsigned)kmod_info->size / PAGE_SIZE,
		    (unsigned long)ml_static_unslide(kmod_info->address),
		    (unsigned)kmod_info->id);
	}

	/* VM protections and wiring for the Aux KC are done at collection loading time */
	if (kc_type != KCKindAuxiliary || flags.resetSegmentsFromVnode) {
		/* if prelinked and primary KC, VM protections are already set */
		result = setVMAttributes(!isPrelinked() || flags.resetSegmentsFromVnode, true);
		if (result != KERN_SUCCESS) {
			goto finish;
		}
	}

#if KASAN
	if (linkedExecutable) {
		kasan_load_kext((vm_offset_t)linkedExecutable->getBytesNoCopy(),
		    linkedExecutable->getLength(), getIdentifierCString());
	}
#else
	if (lookupSection(KASAN_GLOBAL_SEGNAME, KASAN_GLOBAL_SECTNAME)) {
		OSKextLog(this,
		    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "KASAN: cannot load KASAN-ified kext %s on a non-KASAN kernel\n",
		    getIdentifierCString()
		    );
		result = KERN_FAILURE;
		goto finish;
	}
#endif

	result = kOSReturnSuccess;

finish:

#if CONFIG_KXLD
	/* Clear up locally allocated dependency info.
	 */
	for (i = 0; i < num_kxlddeps; ++i) {
		size_t size;

		if (kxlddeps[i].kext_name) {
			size = 1 + strlen(kxlddeps[i].kext_name);
			kfree_data(kxlddeps[i].kext_name, size);
		}
		if (kxlddeps[i].interface_name) {
			size = 1 + strlen(kxlddeps[i].interface_name);
			kfree_data(kxlddeps[i].interface_name, size);
		}
	}
	if (kxlddeps) {
		kfree_type(KXLDDependency, num_kxlddeps, kxlddeps);
	}
#endif // CONFIG_KXLD

	/* We no longer need the unrelocated executable (which the linker
	 * has altered anyhow).
	 */
	setExecutable(NULL);

	if (result != kOSReturnSuccess) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Failed to load executable for kext %s.",
		    getIdentifierCString());

		if (kmod_info && kmod_info->reference_list) {
			kfree_type(kmod_reference_t, num_kmod_refs,
			    kmod_info->reference_list);
		}
		if (isInterface()) {
			kfree_type(kmod_info_t, kmod_info);
			kmod_info = NULL;
		}
		if (kc_type == KCKindUnknown) {
			kmod_info = NULL;
			if (linkedExecutable) {
				linkedExecutable.reset();
			}
		}
	}

	return result;
}

/* static */
void
OSKext::jettisonFileSetLinkeditSegment(kernel_mach_header_t *mh)
{
	kernel_segment_command_t *linkeditseg = NULL;

	linkeditseg = getsegbynamefromheader(mh, SEG_LINKEDIT);
	if (linkeditseg == NULL) {
		panic("FileSet booted with no Linkedit segment");
	}

#if VM_MAPPED_KEXTS
	/* BootKC on x86_64 is not vm mapped */
	ml_static_mfree(linkeditseg->vmaddr, linkeditseg->vmsize);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogGeneralFlag,
	    "Jettisoning fileset Linkedit segments from vmaddr %llx with size %llu",
	    linkeditseg->vmaddr, linkeditseg->vmsize);
#else
	/* BootKC on arm64 is not vm mapped, but is slid */
#if !CONFIG_SPTM
	vm_offset_t linkedit_vmaddr = ml_static_ptovirt((vm_offset_t)(linkeditseg->vmaddr - gVirtBase + gPhysBase));
#else
	vm_offset_t linkedit_vmaddr = linkeditseg->vmaddr;
#endif

	ml_static_mfree(linkedit_vmaddr, (vm_size_t)linkeditseg->vmsize);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogGeneralFlag,
	    "Jettisoning fileset Linkedit segments from vmaddr %llx with size %llu",
	    (unsigned long long)linkedit_vmaddr, (unsigned long long)linkeditseg->vmsize);
#endif /* VM_MAPPED_KEXTS */
}

/*********************************************************************
* The linkedit segment is used by the kext linker for dependency
* resolution, and by dtrace for probe initialization. We can free it
* for non-library kexts, since no kexts depend on non-library kexts
* by definition, once dtrace has been initialized.
*********************************************************************/
void
OSKext::jettisonLinkeditSegment(void)
{
	kernel_mach_header_t     * machhdr = (kernel_mach_header_t *)kmod_info->address;
	kernel_segment_command_t * linkedit = NULL;
	vm_offset_t                start;
	vm_size_t                  linkeditsize, kextsize;
	OSSharedPtr<OSData>        data;
	kc_format                format    = KCFormatUnknown;

	if (isInFileset()) {
		return;
	}

#if NO_KEXTD
	/* We can free symbol tables for all embedded kexts because we don't
	 * support runtime kext linking.
	 */
	if (sKeepSymbols || !isExecutable() || !linkedExecutable || flags.jettisonLinkeditSeg) {
#else
	if (sKeepSymbols || isLibrary() || !isExecutable() || !linkedExecutable || flags.jettisonLinkeditSeg) {
#endif
		goto finish;
	}

	/* Find the linkedit segment.  If it's not the last segment, then freeing
	 * it will fragment the kext into multiple VM regions, which OSKext is not
	 * designed to handle, so we'll have to skip it.
	 */
	linkedit = getsegbynamefromheader(machhdr, SEG_LINKEDIT);
	if (!linkedit) {
		goto finish;
	}

	if (round_page(kmod_info->address + kmod_info->size) !=
	    round_page(linkedit->vmaddr + linkedit->vmsize)) {
		goto finish;
	}

	/* Create a new OSData for the smaller kext object.
	 */
	linkeditsize = round_page(linkedit->vmsize);
	kextsize = kmod_info->size - linkeditsize;
	start = linkedit->vmaddr;

	if (kextsize > UINT_MAX) {
		goto finish;
	}
	data = OSData::withBytesNoCopy((void *)kmod_info->address, (unsigned int)kextsize);
	if (!data) {
		goto finish;
	}

	/* Fix the kmod info and linkedExecutable.
	 */
	kmod_info->size = kextsize;

	/*
	 * Fileset KCs are mapped as a whole by iBoot.
	 * Individual kext executables should not be unmapped by xnu
	 * Doing so may result in panics like rdar://85419651
	 */
	if (PE_get_kc_format(kc_type, &format) && (format == KCFormatFileset)) {
		data->setDeallocFunction(NULL);
	} else { // Not from a Fileset KC
#if VM_MAPPED_KEXTS
		data->setDeallocFunction(osdata_kext_free);
#else
		data->setDeallocFunction(osdata_phys_free);
#endif
	}
	linkedExecutable->setDeallocFunction(NULL);
	linkedExecutable = os::move(data);
	flags.jettisonLinkeditSeg = 1;

	/* Free the linkedit segment.
	 */
#if VM_MAPPED_KEXTS
	kext_free(start, linkeditsize);
#else
	ml_static_mfree(start, linkeditsize);
#endif

finish:
	return;
}

/*********************************************************************
* If there are whole pages that are unused betweem the last section
* of the DATA segment and the end of the DATA segment then we can free
* them
*********************************************************************/
void
OSKext::jettisonDATASegmentPadding(void)
{
	kernel_mach_header_t * mh;
	kernel_segment_command_t * dataSeg;
	kernel_section_t * sec, * lastSec;
	vm_offset_t dataSegEnd, lastSecEnd;
	vm_size_t padSize;

	if (flags.builtin) {
		return;
	}
	mh = (kernel_mach_header_t *)kmod_info->address;

	if (isInFileset()) {
		return;
	}

	dataSeg = getsegbynamefromheader(mh, SEG_DATA);
	if (dataSeg == NULL) {
		return;
	}

	lastSec = NULL;
	sec = firstsect(dataSeg);
	while (sec != NULL) {
		lastSec = sec;
		sec = nextsect(dataSeg, sec);
	}

	if (lastSec == NULL) {
		return;
	}

	if ((dataSeg->vmaddr != round_page(dataSeg->vmaddr)) ||
	    (dataSeg->vmsize != round_page(dataSeg->vmsize))) {
		return;
	}

	dataSegEnd = dataSeg->vmaddr + dataSeg->vmsize;
	lastSecEnd = round_page(lastSec->addr + lastSec->size);

	if (dataSegEnd <= lastSecEnd) {
		return;
	}

	padSize = dataSegEnd - lastSecEnd;

	if (padSize >= PAGE_SIZE) {
#if VM_MAPPED_KEXTS
		kext_free(lastSecEnd, padSize);
#else
		ml_static_mfree(lastSecEnd, padSize);
#endif
	}
}

/*********************************************************************
*********************************************************************/
void
OSKext::setLinkedExecutable(OSData * anExecutable)
{
	if (linkedExecutable) {
		panic("Attempt to set linked executable on kext "
		    "that already has one (%s).\n",
		    getIdentifierCString());
	}
	linkedExecutable.reset(anExecutable, OSRetain);
	return;
}

#if CONFIG_DTRACE
/*********************************************************************
* Go through all loaded kexts and tell them to register with dtrace.
* The instance method only registers if necessary.
*********************************************************************/
/* static */
void
OSKext::registerKextsWithDTrace(void)
{
	uint32_t count = sLoadedKexts->getCount();
	uint32_t i;

	IORecursiveLockLock(sKextLock);

	for (i = 0; i < count; i++) {
		OSKext   * thisKext     = NULL;        // do not release

		thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
		if (!thisKext || !thisKext->isExecutable()) {
			continue;
		}

		thisKext->registerWithDTrace();
	}

	IORecursiveLockUnlock(sKextLock);

	return;
}

extern "C" {
extern int (*dtrace_modload)(struct kmod_info *, uint32_t);
extern int (*dtrace_modunload)(struct kmod_info *);
};

/*********************************************************************
*********************************************************************/
void
OSKext::registerWithDTrace(void)
{
	/* Register kext with dtrace. A dtrace_modload failure should not
	 * prevent a kext from loading, so we ignore the return code.
	 */
	if (!flags.dtraceInitialized && (dtrace_modload != NULL)) {
		uint32_t modflag = 0;
		OSObject * forceInit = getPropertyForHostArch("OSBundleForceDTraceInit");

		if (!sKeepSymbols && kc_type == KCKindPrimary) {
			if (forceInit == kOSBooleanTrue) {
				OSKextLog(this,
				    kOSKextLogBasicLevel |
				    kOSKextLogGeneralFlag,
				    "Ignoring OSBundleForceDTraceInit for Boot KC Kext %s",
				    getIdentifierCString());
				forceInit = kOSBooleanFalse;
			}
			/* Linkedit segment of the Boot KC is gone, make sure fbt_provide_module don't use kernel symbols */
			modflag |= KMOD_DTRACE_NO_KERNEL_SYMS;
		}

		if (forceInit == kOSBooleanTrue) {
			modflag |= KMOD_DTRACE_FORCE_INIT;
		}
		if (flags.builtin) {
			modflag |= KMOD_DTRACE_STATIC_KEXT;
		}

		(void)(*dtrace_modload)(kmod_info, modflag);
		flags.dtraceInitialized = true;
		jettisonLinkeditSegment();
	}
	return;
}
/*********************************************************************
*********************************************************************/
void
OSKext::unregisterWithDTrace(void)
{
	/* Unregister kext with dtrace. A dtrace_modunload failure should not
	 * prevent a kext from loading, so we ignore the return code.
	 */
	if (flags.dtraceInitialized && (dtrace_modunload != NULL)) {
		(void)(*dtrace_modunload)(kmod_info);
		flags.dtraceInitialized = false;
	}
	return;
}
#endif /* CONFIG_DTRACE */


/*********************************************************************
* called only by loadExecutable()
*********************************************************************/
#if !VM_MAPPED_KEXTS
#if defined(__arm__) || defined(__arm64__)
static inline kern_return_t
OSKext_protect(
	kernel_mach_header_t *kext_mh,
	vm_map_t   map,
	vm_map_offset_t    start,
	vm_map_size_t      size,
	vm_prot_t  new_prot,
	boolean_t  set_max,
	kc_kind_t  kc_type)
{
	vm_map_offset_t    end = start + size;

#pragma unused(kext_mh,map,kc_type)
	assert(map == kernel_map);         // we can handle KEXTs arising from the PRELINK segment and no others
	assert(start <= end);
	if (start >= end) {
		return KERN_SUCCESS;         // Punt segments of length zero (e.g., headers) or less (i.e., blunders)
	} else if (set_max) {
		return KERN_SUCCESS;         // Punt set_max, as there's no mechanism to record that state
	} else {
		return ml_static_protect(start, size, new_prot);
	}
}

static inline kern_return_t
OSKext_wire(
	kernel_mach_header_t *kext_mh,
	vm_map_t   map,
	vm_map_offset_t    start,
	vm_map_offset_t    end,
	vm_prot_t  access_type,
	boolean_t       user_wire,
	kc_kind_t       kc_type)
{
#pragma unused(kext_mh,map,start,end,access_type,user_wire,kc_type)
	return KERN_SUCCESS;         // No-op as PRELINK kexts are cemented into physical memory at boot
}
#else
#error Unrecognized architecture
#endif
#else
static inline kern_return_t
OSKext_protect(
	kernel_mach_header_t *kext_mh,
	vm_map_t   map,
	vm_map_offset_t    start,
	vm_map_size_t      size,
	vm_prot_t  new_prot,
	boolean_t  set_max,
	kc_kind_t  kc_type)
{
	vm_map_offset_t    end = start + size;

	if (start == end) {         // 10538581
		return KERN_SUCCESS;
	}
	if (kernel_mach_header_is_in_fileset(kext_mh) && kc_type == KCKindPrimary) {
		/*
		 * XXX: This will probably need to be different for AuxKC and
		 * pageableKC!
		 */
		return ml_static_protect(start, size, new_prot);
	}
	return mach_vm_protect(map, start, size, set_max, new_prot);
}

static inline kern_return_t
OSKext_wire(
	kernel_mach_header_t *kext_mh,
	vm_map_t   map,
	vm_map_offset_t    start,
	vm_map_offset_t    end,
	vm_prot_t  access_type,
	boolean_t       user_wire,
	kc_kind_t       kc_type)
{
	if (kernel_mach_header_is_in_fileset(kext_mh) && kc_type == KCKindPrimary) {
		/* TODO: we may need to hook this for the pageableKC */
		return KERN_SUCCESS;
	}
	return vm_map_wire_kernel(map, start, end, access_type, VM_KERN_MEMORY_KEXT, user_wire);
}
#endif

OSReturn
OSKext::setVMAttributes(bool protect, bool wire)
{
	vm_map_t                    kext_map        = NULL;
	kernel_segment_command_t  * seg             = NULL;
	vm_map_offset_t             start_protect   = 0;
	vm_map_offset_t             start_wire      = 0;
	vm_map_offset_t             end_protect     = 0;
	vm_map_size_t               size_protect    = 0;
	vm_map_offset_t             end_wire        = 0;
	OSReturn                    result          = kOSReturnError;

	if (isInterface() || !declaresExecutable() || flags.builtin) {
		result = kOSReturnSuccess;
		goto finish;
	}

	/* Get the kext's vm map */
	kext_map = kext_get_vm_map(kmod_info);
	if (!kext_map) {
		result = KERN_MEMORY_ERROR;
		goto finish;
	}

#if !VM_MAPPED_KEXTS
	if (getcommandfromheader((kernel_mach_header_t *)kmod_info->address, LC_SEGMENT_SPLIT_INFO)) {
		/* This is a split kext in a prelinked kernelcache; we'll let the
		 * platform code take care of protecting it.  It is already wired.
		 */
		/* TODO: Should this still allow protections for the first segment
		 * to go through, in the event that we have a mix of split and
		 * unsplit kexts?
		 */
		result = KERN_SUCCESS;
		goto finish;
	}

	if (isInFileset() && kc_type != KCKindPageable) {
		// kexts in filesets have protections setup as part of collection loading
		result = KERN_SUCCESS;
		goto finish;
	}
#endif

	/* Protect the headers as read-only; they do not need to be wired */
	result = (protect) ? OSKext_protect((kernel_mach_header_t *)kmod_info->address,
	    kext_map, kmod_info->address,
	    kmod_info->hdr_size, VM_PROT_READ, TRUE, kc_type)
	    : KERN_SUCCESS;
	if (result != KERN_SUCCESS) {
		goto finish;
	}

	/* Set the VM protections and wire down each of the segments */
	seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
	while (seg) {
#if __arm__
		/* We build all ARM kexts, so we can ensure they are aligned */
		assert((seg->vmaddr & PAGE_MASK) == 0);
		assert((seg->vmsize & PAGE_MASK) == 0);
#endif

		/*
		 * For the non page aligned segments, the range calculation for protection
		 * and wiring differ as follows:
		 *
		 * Protection: The non page aligned data at the start or at the end of the
		 * segment is excluded from the protection. This exclusion is needed to make
		 * sure OSKext_protect is not called twice on same page, if the page is shared
		 * between two segments.
		 *
		 * Wiring: The non page aligned data at the start or at the end of the
		 * segment is included in the wiring range, this inclusion is needed to make sure
		 * all the data of the segment is wired.
		 */
		start_protect = round_page(seg->vmaddr);
		end_protect = trunc_page(seg->vmaddr + seg->vmsize);
		size_protect = end_protect - start_protect;

		start_wire = trunc_page(seg->vmaddr);
		end_wire = round_page(seg->vmaddr + seg->vmsize);

		/*
		 * Linkedit and Linkinfo for the Pageable KC and the Aux KC are shared
		 * across kexts and data from kexts is not page aligned
		 */
		if (protect && (end_protect > start_protect) &&
		    ((strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)) != 0 &&
		    strncmp(seg->segname, SEG_LINKINFO, sizeof(seg->segname)) != 0) ||
		    (kc_type != KCKindPageable && kc_type != KCKindAuxiliary))) {
			result = OSKext_protect((kernel_mach_header_t *)kmod_info->address,
			    kext_map, start_protect, size_protect, seg->maxprot, TRUE, kc_type);
			if (result != KERN_SUCCESS) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Kext %s failed to set maximum VM protections "
				    "for segment %s - 0x%x.",
				    getIdentifierCString(), seg->segname, (int)result);
				goto finish;
			}

			result = OSKext_protect((kernel_mach_header_t *)kmod_info->address,
			    kext_map, start_protect, size_protect, seg->initprot, FALSE, kc_type);
			if (result != KERN_SUCCESS) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Kext %s failed to set initial VM protections "
				    "for segment %s - 0x%x.",
				    getIdentifierCString(), seg->segname, (int)result);
				goto finish;
			}
		}

		if (segmentShouldBeWired(seg) && wire) {
			result = OSKext_wire((kernel_mach_header_t *)kmod_info->address,
			    kext_map, start_wire, end_wire, seg->initprot, FALSE, kc_type);
			if (result != KERN_SUCCESS) {
				goto finish;
			}
		}

		seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
	}

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
boolean_t
OSKext::segmentShouldBeWired(kernel_segment_command_t *seg)
{
	return sKeepSymbols || (strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)) &&
	       strncmp(seg->segname, SEG_LINKINFO, sizeof(seg->segname)));
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::validateKextMapping(bool startFlag)
{
	OSReturn                              result      = kOSReturnError;
	const char                          * whichOp = startFlag ? "start" : "stop";
	kern_return_t                         kern_result = 0;
	vm_map_t                              kext_map    = NULL;
	kernel_segment_command_t            * seg         = NULL;
	mach_vm_address_t                     address     = 0;
	mach_vm_size_t                        size        = 0;
	uint32_t                              depth       = 0;
	uint64_t                              kext_segbase = 0;
	uint64_t                              kext_segsize = 0;
	mach_msg_type_number_t                count;
	vm_region_submap_short_info_data_64_t info;
	uintptr_t                             kext_slide = PE_get_kc_slide(kc_type);

	if (flags.builtin) {
		return kOSReturnSuccess;
	}

	count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
	bzero(&info, sizeof(info));

	// xxx - do we need a distinct OSReturn value for these or is "bad data"
	// xxx - sufficient?

	/* Verify that the kmod_info and start/stop pointers are non-NULL.
	 */
	if (!kmod_info) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s - NULL kmod_info pointer.",
		    getIdentifierCString());
		result = kOSKextReturnBadData;
		goto finish;
	}

	if (startFlag) {
		address = (mach_vm_address_t)kmod_info->start;
	} else {
		address = (mach_vm_address_t)kmod_info->stop;
	}

	if (!address) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s - NULL module %s pointer.",
		    getIdentifierCString(), whichOp);
		result = kOSKextReturnBadData;
		goto finish;
	}

	kext_map = kext_get_vm_map(kmod_info);
	depth = (kernel_map == kext_map) ? 1 : 2;
	if (isInFileset()) {
#if defined(HAS_APPLE_PAC)
		address = (mach_vm_address_t)ptrauth_auth_data((void*)address, ptrauth_key_function_pointer, 0);
#endif /* defined(HAS_APPLE_PAC) */
	}

	/* Verify that the start/stop function lies within the kext's address range.
	 */
	if (getcommandfromheader((kernel_mach_header_t *)kmod_info->address, LC_SEGMENT_SPLIT_INFO) ||
	    isInFileset()) {
		/* This will likely be how we deal with split kexts; walk the segments to
		 * check that the function lies inside one of the segments of this kext.
		 */
		for (seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
		    seg != NULL;
		    seg = nextsegfromheader((kernel_mach_header_t *)kmod_info->address, seg)) {
			if ((address >= seg->vmaddr) && address < (seg->vmaddr + seg->vmsize)) {
				kext_segbase = seg->vmaddr;
				kext_segsize = seg->vmsize;
				break;
			}
		}

		if (!seg) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s module %s pointer is outside of kext range "
			    "(%s %p - kext starts at %p).",
			    getIdentifierCString(),
			    whichOp,
			    whichOp,
			    (void *)(((uintptr_t)address) - kext_slide),
			    (void *)(((uintptr_t)kmod_info->address) - kext_slide));
			result = kOSKextReturnBadData;
			goto finish;
		}

		seg = NULL;
	} else {
		if (address < kmod_info->address + kmod_info->hdr_size ||
		    kmod_info->address + kmod_info->size <= address) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s module %s pointer is outside of kext range "
			    "(%s %p - kext at %p-%p).",
			    getIdentifierCString(),
			    whichOp,
			    whichOp,
			    (void *)(((uintptr_t)address) - kext_slide),
			    (void *)(((uintptr_t)kmod_info->address) - kext_slide),
			    (void *)((((uintptr_t)kmod_info->address) - kext_slide) + kmod_info->size));
			result = kOSKextReturnBadData;
			goto finish;
		}
	}

	/* Only do these checks before calling the start function;
	 * If anything goes wrong with the mapping while the kext is running,
	 * we'll likely have panicked well before any attempt to stop the kext.
	 */
	if (startFlag) {
		if (!isInFileset() || kc_type != KCKindPrimary) {
			/*
			 * Verify that the start/stop function is executable.
			 */
			kern_result = mach_vm_region_recurse(kernel_map, &address, &size, &depth,
			    (vm_region_recurse_info_t)&info, &count);
			if (kern_result != KERN_SUCCESS) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Kext %s - bad %s pointer %p.",
				    getIdentifierCString(),
				    whichOp, (void *)ml_static_unslide(address));
				result = kOSKextReturnBadData;
				goto finish;
			}
		} else {
			/*
			 * Since kexts loaded from the primary KC are held in memory
			 * allocated by efiboot, we cannot use mach_vm_region_recurse() to
			 * discover that memory's protection flags.  Instead, we need to
			 * get that information from the kernel pmap itself.  Above, we
			 * (potentially) saved the size of the segment in which the address
			 * in question was located.  If we have a non-zero size, verify
			 * that all pages in the (address, address + kext_segsize) range
			 * are marked executable.  If we somehow did not record the size
			 * (or the base) just verify the single page that includes the address.
			 */
			if (kext_segbase == 0 || kext_segsize == 0) {
				kext_segbase = address & ~(uint64_t)PAGE_MASK;
				kext_segsize = PAGE_SIZE;
			}
		}

#if VM_MAPPED_KEXTS
		if (((!isInFileset() || kc_type != KCKindPrimary) && !(info.protection & VM_PROT_EXECUTE)) ||
		    ((isInFileset() && kc_type == KCKindPrimary) &&
		    ml_static_verify_page_protections(kext_segbase, kext_segsize, VM_PROT_EXECUTE) != KERN_SUCCESS)) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s - memory region containing module %s function "
			    "is not executable.",
			    getIdentifierCString(), whichOp);
			result = kOSKextReturnBadData;
			goto finish;
		}
#endif

		/* Verify that the kext's segments are backed by physical memory.
		 */
		seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
		while (seg) {
			if (!verifySegmentMapping(seg)) {
				result = kOSKextReturnBadData;
				goto finish;
			}

			seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
		}
	}

	result = kOSReturnSuccess;
finish:
	return result;
}

/*********************************************************************
*********************************************************************/
boolean_t
OSKext::verifySegmentMapping(kernel_segment_command_t *seg)
{
	mach_vm_address_t address = 0;

	if (seg->vmsize > UINT32_MAX) {
		return false;
	}

	if (!segmentShouldBeWired(seg)) {
		return true;
	}

	for (address = seg->vmaddr;
	    address < round_page(seg->vmaddr + seg->vmsize);
	    address += PAGE_SIZE) {
		if (!pmap_find_phys(kernel_pmap, (vm_offset_t)address)) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s - page %p is not backed by physical memory.",
			    getIdentifierCString(),
			    (void *)address);
			return false;
		}
	}

	return true;
}

/*********************************************************************
*********************************************************************/
static void
OSKextLogKextInfo(OSKext *aKext, uint64_t address, uint64_t size, firehose_tracepoint_code_t code)
{
	uint64_t                            stamp = 0;
	firehose_tracepoint_id_u            trace_id;
	struct firehose_trace_uuid_info_s   uuid_info_s;
	firehose_trace_uuid_info_t          uuid_info = &uuid_info_s;
	size_t                              uuid_info_len = sizeof(struct firehose_trace_uuid_info_s);
	OSSharedPtr<OSData>                 uuid_data;

	stamp = firehose_tracepoint_time(firehose_activity_flags_default);
	trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata, _firehose_tracepoint_type_metadata_kext, (firehose_tracepoint_flags_t)0, code);

	uuid_data = aKext->copyTextUUID();
	if (uuid_data) {
		memcpy(uuid_info->ftui_uuid, uuid_data->getBytesNoCopy(), sizeof(uuid_info->ftui_uuid));
	}

	uuid_info->ftui_size    = size;
	if (aKext->isDriverKit()) {
		uuid_info->ftui_address = address;
	} else {
		uuid_info->ftui_address = ml_static_unslide(address);
	}
	os_log_encoded_metadata(trace_id, stamp, uuid_info, uuid_info_len);
	return;
}

void
OSKext::OSKextLogDriverKitInfoLoad(OSKext *kext)
{
	OSKextLogKextInfo(kext, kext->getLoadTag(), 1, firehose_tracepoint_code_load);
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::start(bool startDependenciesFlag)
{
	OSReturn                            result = kOSReturnError;
	kern_return_t                       (* startfunc)(kmod_info_t *, void *);
	unsigned int                        i, count;
	void                              * kmodStartData = NULL;

	if (isStarted() || isInterface() || isKernelComponent()) {
		result = kOSReturnSuccess;
		goto finish;
	}

	if (!isLoaded()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Attempt to start nonloaded kext %s.",
		    getIdentifierCString());
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	if (!sLoadEnabled) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext loading is disabled (attempt to start kext %s).",
		    getIdentifierCString());
		result = kOSKextReturnDisabled;
		goto finish;
	}

	result = validateKextMapping(/* start? */ true);
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	startfunc = kmod_info->start;

	count = getNumDependencies();
	for (i = 0; i < count; i++) {
		OSKext * dependency = OSDynamicCast(OSKext, dependencies->getObject(i));
		if (dependency == NULL) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s start - internal error, dependency disappeared.",
			    getIdentifierCString());
			goto finish;
		}
		if (!dependency->isStarted()) {
			if (startDependenciesFlag) {
				OSReturn dependencyResult =
				    dependency->start(startDependenciesFlag);
				if (dependencyResult != KERN_SUCCESS) {
					OSKextLog(this,
					    kOSKextLogErrorLevel |
					    kOSKextLogLoadFlag,
					    "Kext %s start - dependency %s failed to start (error 0x%x).",
					    getIdentifierCString(),
					    dependency->getIdentifierCString(),
					    dependencyResult);
					goto finish;
				}
			} else {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Not starting %s - dependency %s not started yet.",
				    getIdentifierCString(),
				    dependency->getIdentifierCString());
				result = kOSKextReturnStartStopError;         // xxx - make new return?
				goto finish;
			}
		}
	}

	OSKextLog(this,
	    kOSKextLogDetailLevel |
	    kOSKextLogLoadFlag,
	    "Kext %s calling module start function.",
	    getIdentifierCString());

	flags.starting = 1;

	// Drop a log message so logd can grab the needed information to decode this kext
	OSKextLogKextInfo(this, kmod_info->address, kmod_info->size, firehose_tracepoint_code_load);
	result = OSRuntimeInitializeCPP(this);
	if (result == KERN_SUCCESS) {
		result = startfunc(kmod_info, kmodStartData);
	}

	flags.starting = 0;

	/* On success overlap the setting of started/starting. On failure just
	 * clear starting.
	 */
	if (result == KERN_SUCCESS) {
		flags.started = 1;

		// xxx - log start error from kernel?
		OSKextLog(this,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s is now started.",
		    getIdentifierCString());
	} else {
		invokeOrCancelRequestCallbacks(
			/* result not actually used */ kOSKextReturnStartStopError,
			/* invokeFlag */ false);
		OSKextLog(this,
		    kOSKextLogWarningLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s did not start (return code 0x%x).",
		    getIdentifierCString(), result);
	}

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::canUnloadKextWithIdentifier(
	OSString * kextIdentifier,
	bool       checkClassesFlag)
{
	bool     result = false;
	OSKext * aKext  = NULL;        // do not release

	IORecursiveLockLock(sKextLock);

	aKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));

	if (!aKext) {
		goto finish;         // can't unload what's not loaded
	}

	if (aKext->isLoaded()) {
		if (aKext->getRetainCount() > kOSKextMinLoadedRetainCount) {
			goto finish;
		}
		if (checkClassesFlag && aKext->hasOSMetaClassInstances()) {
			goto finish;
		}
	}

	result = true;

finish:
	IORecursiveLockUnlock(sKextLock);
	return result;
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::stop(void)
{
	OSReturn result = kOSReturnError;
	kern_return_t (*stopfunc)(kmod_info_t *, void *);

	if (!isStarted() || isInterface()) {
		result = kOSReturnSuccess;
		goto finish;
	}

	if (!isLoaded()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Attempt to stop nonloaded kext %s.",
		    getIdentifierCString());
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	/* Refuse to stop if we have clients or instances. It is up to
	 * the caller to make sure those aren't true.
	 */
	if (getRetainCount() > kOSKextMinLoadedRetainCount) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s - C++ instances; can't stop.",
		    getIdentifierCString());
		result = kOSKextReturnInUse;
		goto finish;
	}

	if (getRetainCount() > kOSKextMinLoadedRetainCount) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s - has references (linkage or tracking object); "
		    "can't stop.",
		    getIdentifierCString());
		result = kOSKextReturnInUse;
		goto finish;
	}

	/* Note: If validateKextMapping fails on the stop & unload path,
	 * we are in serious trouble and a kernel panic is likely whether
	 * we stop & unload the kext or not.
	 */
	result = validateKextMapping(/* start? */ false);
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	stopfunc = kmod_info->stop;
	if (stopfunc) {
		OSKextLog(this,
		    kOSKextLogDetailLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s calling module stop function.",
		    getIdentifierCString());

		flags.stopping = 1;

		result = stopfunc(kmod_info, /* userData */ NULL);
		if (result == KERN_SUCCESS) {
			result = OSRuntimeFinalizeCPP(this);
		}

		flags.stopping = 0;

		if (result == KERN_SUCCESS) {
			flags.started = 0;

			OSKextLog(this,
			    kOSKextLogDetailLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s is now stopped and ready to unload.",
			    getIdentifierCString());
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s did not stop (return code 0x%x).",
			    getIdentifierCString(), result);
			result = kOSKextReturnStartStopError;
		}
	}

finish:
	// Drop a log message so logd can update this kext's metadata
	OSKextLogKextInfo(this, kmod_info->address, kmod_info->size, firehose_tracepoint_code_unload);
	return result;
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::unload(void)
{
	OSReturn        result = kOSReturnError;
	unsigned int    index;
	uint32_t        num_kmod_refs = 0;
	OSKextAccount * freeAccount;
	bool            in_fileset = false;

	if (!sUnloadEnabled) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext unloading is disabled (%s).",
		    this->getIdentifierCString());

		result = kOSKextReturnDisabled;
		goto finish;
	}

	// cache this result so we don't need to access the kmod_info after
	// it's been potentially free'd
	in_fileset = isInFileset();

	/* Refuse to unload if we have clients or instances. It is up to
	 * the caller to make sure those aren't true.
	 */
	if (getRetainCount() > kOSKextMinLoadedRetainCount) {
		// xxx - Don't log under errors? this is more of an info thing
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogKextBookkeepingFlag,
		    "Can't unload kext %s; outstanding references %d (linkage or tracking object).",
		    getIdentifierCString(), getRetainCount());
		result = kOSKextReturnInUse;
		goto finish;
	}

	if (isDriverKit()) {
		index = sLoadedDriverKitKexts->getNextIndexOfObject(this, 0);
		if (index != (unsigned int)-1) {
			sLoadedDriverKitKexts->removeObject(index);
			OSKextLogKextInfo(this, loadTag, 1, firehose_tracepoint_code_unload);
			loadTag = 0;
		}
	}

	if (!isLoaded()) {
		result = kOSReturnSuccess;
		goto finish;
	}

	if (isKernelComponent()) {
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	if (metaClasses && !OSMetaClass::removeClasses(metaClasses.get())) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Can't unload kext %s; classes have instances:",
		    getIdentifierCString());
		reportOSMetaClassInstances(kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag);
		result = kOSKextReturnInUse;
		goto finish;
	}

	/* Note that the kext is unloading before running any code that
	 * might be in the kext (request callbacks, module stop function).
	 * We will deny certain requests made against a kext in the process
	 * of unloading.
	 */
	flags.unloading = 1;

	/* Update the string describing the last kext to unload in case we panic.
	 */
	savePanicString(/* isLoading */ false);

	if (isStarted()) {
		result = stop();
		if (result != KERN_SUCCESS) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s can't unload - module stop returned 0x%x.",
			    getIdentifierCString(), (unsigned)result);
			result = kOSKextReturnStartStopError;
			goto finish;
		}
	}

	OSKextLog(this,
	    kOSKextLogProgressLevel |
	    kOSKextLogLoadFlag,
	    "Kext %s unloading.",
	    getIdentifierCString());

	{
		struct list_head *p;
		struct list_head *prev;
		struct list_head *next;
		for (p = pendingPgoHead.next; p != &pendingPgoHead; p = next) {
			OSKextGrabPgoStruct *s = container_of(p, OSKextGrabPgoStruct, list_head);
			s->err = OSKextGrabPgoDataLocked(this, s->metadata, instance_uuid, s->pSize, s->pBuffer, s->bufferSize);
			prev = p->prev;
			next = p->next;
			prev->next = next;
			next->prev = prev;
			p->prev = p;
			p->next = p;
			IORecursiveLockWakeup(sKextLock, s, false);
		}
	}


	/* Even if we don't call the stop function, we want to be sure we
	 * have no OSMetaClass references before unloading the kext executable
	 * from memory. OSMetaClasses may have pointers into the kext executable
	 * and that would cause a panic on OSKext::free() when metaClasses is freed.
	 */
	if (metaClasses) {
		metaClasses->flushCollection();
	}
	(void) OSRuntimeFinalizeCPP(this);

	/* Remove the kext from the list of loaded kexts, patch the gap
	 * in the kmod_info_t linked list, and reset "kmod" to point to the
	 * last loaded kext that isn't the fake kernel kext (sKernelKext).
	 */
	index = sLoadedKexts->getNextIndexOfObject(this, 0);
	if (index != (unsigned int)-1) {
		sLoadedKexts->removeObject(index);

		OSKext * nextKext = OSDynamicCast(OSKext,
		    sLoadedKexts->getObject(index));

		if (nextKext) {
			if (index > 0) {
				OSKext * gapKext = OSDynamicCast(OSKext,
				    sLoadedKexts->getObject(index - 1));

				nextKext->kmod_info->next = gapKext->kmod_info;
			} else {         /* index == 0 */
				nextKext->kmod_info->next = NULL;
			}
		}

		OSKext * lastKext = OSDynamicCast(OSKext, sLoadedKexts->getLastObject());
		if (lastKext && !lastKext->isKernel()) {
			kmod = lastKext->kmod_info;
		} else {
			kmod = NULL;         // clear the global kmod variable
		}
	}

	/* Clear out the kmod references that we're keeping for compatibility
	 * with current panic backtrace code & kgmacros.
	 * xxx - will want to update those bits sometime and remove this.
	 */
	num_kmod_refs = getNumDependencies();
	if (num_kmod_refs && kmod_info && kmod_info->reference_list) {
		for (uint32_t refIndex = 0; refIndex < num_kmod_refs; refIndex++) {
			kmod_reference_t * ref = &(kmod_info->reference_list[refIndex]);
			ref->info->reference_count--;
		}
		kfree_type(kmod_reference_t, num_kmod_refs,
		    kmod_info->reference_list);
	}

#if CONFIG_DTRACE
	unregisterWithDTrace();
#endif /* CONFIG_DTRACE */

	notifyKextUnloadObservers(this);

	freeAccount = NULL;
	lck_ticket_lock(sKextAccountsLock, sKextAccountsLockGrp);
	account->kext = NULL;
	if (account->site.tag) {
		account->site.flags |= VM_TAG_UNLOAD;
	} else {
		freeAccount = account;
	}

#if DEVELOPMENT || DEBUG
	assertf(account->task_refgrp.grp_count == 0,
	    "unloading a kext with active task references");
#endif /* DEVELOPMENT || DEBUG */

	lck_ticket_unlock(sKextAccountsLock);
	if (freeAccount) {
		IOFreeType(freeAccount, OSKextAccount);
	}

	/* Unwire and free the linked executable.
	 */
	if (linkedExecutable) {
#if KASAN
		kasan_unload_kext((vm_offset_t)linkedExecutable->getBytesNoCopy(), linkedExecutable->getLength());
#endif

#if VM_MAPPED_KEXTS
		if (!isInterface() && (!in_fileset || flags.resetSegmentsFromVnode)) {
			kernel_segment_command_t *seg = NULL;
			vm_map_t kext_map = kext_get_vm_map(kmod_info);

			if (!kext_map) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Failed to free kext %s; couldn't find the kext map.",
				    getIdentifierCString());
				result = kOSKextReturnInternalError;
				goto finish;
			}

			OSKextLog(this,
			    kOSKextLogProgressLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s unwiring and unmapping linked executable.",
			    getIdentifierCString());

			seg = firstsegfromheader((kernel_mach_header_t *)kmod_info->address);
			while (seg) {
				if (segmentShouldBeWired(seg)) {
					vm_map_offset_t start_wire = trunc_page(seg->vmaddr);
					vm_map_offset_t end_wire = round_page(seg->vmaddr + seg->vmsize);

					result = vm_map_unwire(kext_map, start_wire,
					    end_wire, FALSE);
					if (result != KERN_SUCCESS) {
						OSKextLog(this,
						    kOSKextLogErrorLevel |
						    kOSKextLogLoadFlag,
						    "Failed to unwire kext %s.",
						    getIdentifierCString());
						result = kOSKextReturnInternalError;
						goto finish;
					}
				}

				seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg);
			}
#if defined(__x86_64__) || defined(__i386__)
			if (in_fileset && flags.resetSegmentsFromVnode) {
				IORecursiveLockLock(sKextLock);
				resetKCFileSetSegments();
				IORecursiveLockUnlock(sKextLock);
			}
#endif // (__x86_64__) || defined(__i386__)
		}
#endif /* VM_MAPPED_KEXTS */
		if (flags.resetSegmentsFromImmutableCopy) {
			result = resetMutableSegments();
			if (result != kOSReturnSuccess) {
				OSKextLog(this,
				    kOSKextLogErrorLevel |
				    kOSKextLogLoadFlag,
				    "Failed to reset kext %s.",
				    getIdentifierCString());
				result = kOSKextReturnInternalError;
				goto finish;
			}
		}
		if (kc_type == KCKindUnknown) {
			linkedExecutable.reset();
		}
	}

	/* An interface kext has a fake kmod_info that was allocated,
	 * so we have to free it.
	 */
	if (isInterface()) {
		kfree_type(kmod_info_t, kmod_info);
		kmod_info = NULL;
	}

	if (!in_fileset) {
		kmod_info = NULL;
	}

	flags.loaded = false;
	flushDependencies();

	/* save a copy of the bundle ID for us to check when deciding to
	 * rebuild the kernel cache file.  If a kext was already in the kernel
	 * cache and unloaded then later loaded we do not need to rebuild the
	 * kernel cache.  9055303
	 */
	if (isPrelinked()) {
		if (!_OSKextInUnloadedPrelinkedKexts(bundleID.get())) {
			IORecursiveLockLock(sKextLock);
			if (sUnloadedPrelinkedKexts) {
				sUnloadedPrelinkedKexts->setObject(bundleID.get());
			}
			IORecursiveLockUnlock(sKextLock);
		}
	}

	OSKextLog(this,
	    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
	    "Kext %s unloaded.", getIdentifierCString());

	queueKextNotification(kKextRequestPredicateUnloadNotification,
	    OSDynamicCast(OSString, bundleID.get()), getDextUniqueID());

finish:
	OSKext::saveLoadedKextPanicList();
	OSKext::updateLoadedKextSummaries();

	flags.unloading = 0;
	return result;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::queueKextNotification(
	const char * notificationName,
	OSString   * kextIdentifier,
	OSData     * dextUniqueIdentifier)
{
	OSReturn          result               = kOSReturnError;
	OSSharedPtr<OSDictionary>    loadRequest;

	if (!kextIdentifier) {
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	/* Create a new request unless one is already sitting
	 * in sKernelRequests for this bundle identifier
	 */
	result = _OSKextCreateRequest(notificationName, loadRequest);
	if (result != kOSReturnSuccess) {
		goto finish;
	}
	if (!_OSKextSetRequestArgument(loadRequest.get(),
	    kKextRequestArgumentBundleIdentifierKey, kextIdentifier)) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}
	if (NULL != dextUniqueIdentifier) {
		if (!_OSKextSetRequestArgument(loadRequest.get(),
		    kKextRequestArgumentDriverUniqueIdentifier, dextUniqueIdentifier)) {
			result = kOSKextReturnNoMemory;
			goto finish;
		}
	}
	if (!sKernelRequests->setObject(loadRequest.get())) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	/* We might want to only queue the notification if the IOKit daemon is active,
	 * but that wouldn't work for embedded. Note that we don't care if
	 * the ping immediately succeeds here so don't do anything with the
	 * result of this call.
	 */
	OSKext::pingIOKitDaemon();

	result = kOSReturnSuccess;

finish:
	return result;
}


#if CONFIG_KXLD
/*********************************************************************
*********************************************************************/
static void
_OSKextConsiderDestroyingLinkContext(
	__unused thread_call_param_t p0,
	__unused thread_call_param_t p1)
{
	/* Take multiple locks in the correct order.
	 */
	IORecursiveLockLock(sKextLock);
	IORecursiveLockLock(sKextInnerLock);

	/* The first time we destroy the kxldContext is in the first
	 * OSKext::considerUnloads() call, which sets sConsiderUnloadsCalled
	 * before calling this function. Thereafter any call to this function
	 * will actually destroy the context.
	 */
	if (sConsiderUnloadsCalled && sKxldContext) {
		kxld_destroy_context(sKxldContext);
		sKxldContext = NULL;
	}

	/* Free the thread_call that was allocated to execute this function.
	 */
	if (sDestroyLinkContextThread) {
		if (!thread_call_free(sDestroyLinkContextThread)) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogGeneralFlag,
			    "thread_call_free() failed for kext link context.");
		}
		sDestroyLinkContextThread = NULL;
	}

	IORecursiveLockUnlock(sKextInnerLock);
	IORecursiveLockUnlock(sKextLock);

	return;
}

/*********************************************************************
* Destroying the kxldContext requires checking variables under both
* sKextInnerLock and sKextLock, so we do it on a separate thread
* to avoid deadlocks with IOService, with which OSKext has a reciprocal
* call relationship.
*
* This function must be invoked with sKextInnerLock held.
* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
void
OSKext::considerDestroyingLinkContext(void)
{
	IORecursiveLockLock(sKextInnerLock);

	/* If we have already queued a thread to destroy the link context,
	 * don't bother resetting; that thread will take care of it.
	 */
	if (sDestroyLinkContextThread) {
		goto finish;
	}

	/* The function to be invoked in the thread will deallocate
	 * this thread_call, so don't share it around.
	 */
	sDestroyLinkContextThread = thread_call_allocate(
		&_OSKextConsiderDestroyingLinkContext, NULL);
	if (!sDestroyLinkContextThread) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogLinkFlag,
		    "Can't create thread to destroy kext link context.");
		goto finish;
	}

	thread_call_enter(sDestroyLinkContextThread);

finish:
	IORecursiveLockUnlock(sKextInnerLock);
	return;
}

#else // !CONFIG_KXLD

/* static */
void
OSKext::considerDestroyingLinkContext(void)
{
	return;
}

#endif // CONFIG_KXLD

#if PRAGMA_MARK
#pragma mark Autounload
#endif
/*********************************************************************
* This is a static method because the kext will be deallocated if it
* does unload!
*********************************************************************/
/* static */
OSReturn
OSKext::autounloadKext(OSKext * aKext)
{
	OSReturn result = kOSKextReturnInUse;

#if NO_KEXTD
	/*
	 * Do not unload prelinked kexts on platforms that do not have an
	 * IOKit daemon as there is no way to reload the kext or restart
	 * matching.
	 */
	if (aKext->isPrelinked()) {
		goto finish;
	}
#endif /* defined(__x86_64__) */

	/* Check for external references to this kext (usu. dependents),
	 * instances of defined classes (or classes derived from them),
	 * outstanding requests.
	 */
	if ((aKext->getRetainCount() > kOSKextMinLoadedRetainCount) ||
	    !aKext->flags.autounloadEnabled ||
	    aKext->isKernelComponent()) {
		goto finish;
	}

	/* Skip a delay-autounload kext, once.
	 */
	if (aKext->flags.delayAutounload) {
		OSKextLog(aKext,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag,
		    "Kext %s has delayed autounload set; skipping and clearing flag.",
		    aKext->getIdentifierCString());
		aKext->flags.delayAutounload = 0;
		goto finish;
	}

	if (aKext->hasOSMetaClassInstances() ||
	    aKext->countRequestCallbacks()) {
		goto finish;
	}

	result = OSKext::removeKext(aKext);

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
void
_OSKextConsiderUnloads(
	__unused thread_call_param_t p0,
	__unused thread_call_param_t p1)
{
	bool         didUnload = false;
	unsigned int count, i;

	/* Take multiple locks in the correct order
	 * (note also sKextSummaries lock further down).
	 */
	IORecursiveLockLock(sKextLock);
	IORecursiveLockLock(sKextInnerLock);

	OSKext::flushNonloadedKexts(/* flushPrelinkedKexts */ true);

	/* If the system is powering down, don't try to unload anything.
	 */
	if (sSystemSleep) {
		goto finish;
	}

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel | kOSKextLogLoadFlag,
	    "Checking for unused kexts to autounload.");

	/*****
	 * Remove any request callbacks marked as stale,
	 * and mark as stale any currently in flight.
	 */
	count = sRequestCallbackRecords->getCount();
	if (count) {
		i = count - 1;
		do {
			OSDictionary * callbackRecord = OSDynamicCast(OSDictionary,
			    sRequestCallbackRecords->getObject(i));
			if (callbackRecord) {
				OSBoolean * stale = OSDynamicCast(OSBoolean,
				    callbackRecord->getObject(kKextRequestStaleKey));

				if (stale == kOSBooleanTrue) {
					OSKext::invokeRequestCallback(callbackRecord,
					    kOSKextReturnTimeout);
				} else {
					callbackRecord->setObject(kKextRequestStaleKey,
					    kOSBooleanTrue);
				}
			}
		} while (i--);
	}

	/*****
	 * Make multiple passes through the array of loaded kexts until
	 * we don't unload any. This handles unwinding of dependency
	 * chains. We have to go *backwards* through the array because
	 * kexts are removed from it when unloaded, and we cannot make
	 * a copy or we'll mess up the retain counts we rely on to
	 * check whether a kext will unload. If only we could have
	 * nonretaining collections like CF has....
	 */
	do {
		didUnload = false;

		count = sLoadedKexts->getCount();
		if (count) {
			i = count - 1;
			do {
				OSKext * thisKext = OSDynamicCast(OSKext,
				    sLoadedKexts->getObject(i));
				didUnload |= (kOSReturnSuccess == OSKext::autounloadKext(thisKext));
			} while (i--);
		}
	} while (didUnload);

finish:
	sConsiderUnloadsPending = false;
	sConsiderUnloadsExecuted = true;

	(void) OSKext::considerRebuildOfPrelinkedKernel();

	IORecursiveLockUnlock(sKextInnerLock);
	IORecursiveLockUnlock(sKextLock);

	return;
}

/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
void
OSKext::considerUnloads(Boolean rescheduleOnlyFlag)
{
	AbsoluteTime when;

	IORecursiveLockLock(sKextInnerLock);

	if (!sUnloadCallout) {
		sUnloadCallout = thread_call_allocate(&_OSKextConsiderUnloads, NULL);
	}

	/* we only reset delay value for unloading if we already have something
	 * pending.  rescheduleOnlyFlag should not start the count down.
	 */
	if (rescheduleOnlyFlag && !sConsiderUnloadsPending) {
		goto finish;
	}

	thread_call_cancel(sUnloadCallout);
	if (OSKext::getAutounloadEnabled() && !sSystemSleep
#if !NO_KEXTD
	    && sIOKitDaemonActive
#endif
	    ) {
		clock_interval_to_deadline(sConsiderUnloadDelay,
		    1000 * 1000 * 1000, &when);

		OSKextLog(/* kext */ NULL,
		    kOSKextLogProgressLevel |
		    kOSKextLogLoadFlag,
		    "%scheduling %sscan for unused kexts in %lu seconds.",
		    sConsiderUnloadsPending ? "Res" : "S",
		    sConsiderUnloadsCalled ? "" : "initial ",
		    (unsigned long)sConsiderUnloadDelay);

		sConsiderUnloadsPending = true;
		thread_call_enter_delayed(sUnloadCallout, when);
	}

finish:
	/* The kxld context should be reused throughout boot.  We mark the end of
	 * period as the first time considerUnloads() is called, and we destroy
	 * the first kxld context in that function.  Afterwards, it will be
	 * destroyed in flushNonloadedKexts.
	 */
	if (!sConsiderUnloadsCalled) {
		sConsiderUnloadsCalled = true;
		OSKext::considerDestroyingLinkContext();
	}

	IORecursiveLockUnlock(sKextInnerLock);
	return;
}

/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
extern "C" {
IOReturn OSKextSystemSleepOrWake(UInt32 messageType);
IOReturn
OSKextSystemSleepOrWake(UInt32 messageType)
{
	IORecursiveLockLock(sKextInnerLock);

	/* If the system is going to sleep, cancel the reaper thread timer,
	 * and note that we're in a sleep state in case it just fired but hasn't
	 * taken the lock yet. If we are coming back from sleep, just
	 * clear the sleep flag; IOService's normal operation will cause
	 * unloads to be considered soon enough.
	 */
	if (messageType == kIOMessageSystemWillSleep) {
		if (sUnloadCallout) {
			thread_call_cancel(sUnloadCallout);
		}
		sSystemSleep = true;
		AbsoluteTime_to_scalar(&sLastWakeTime) = 0;
	} else if (messageType == kIOMessageSystemHasPoweredOn) {
		sSystemSleep = false;
		clock_get_uptime(&sLastWakeTime);
	}
	IORecursiveLockUnlock(sKextInnerLock);

	return kIOReturnSuccess;
}
};


#if PRAGMA_MARK
#pragma mark Prelinked Kernel
#endif

#ifdef CONFIG_KXLD
/*********************************************************************
* Do not access sConsiderUnloads... variables other than
* sConsiderUnloadsExecuted in this function. They are guarded by a
* different lock.
*********************************************************************/
/* static */
void
OSKext::considerRebuildOfPrelinkedKernel(void)
{
	static bool     requestedPrelink        = false;
	OSReturn        checkResult             = kOSReturnError;
	OSSharedPtr<OSDictionary>         prelinkRequest;
	OSSharedPtr<OSCollectionIterator> kextIterator;
	const OSSymbol * thisID                 = NULL;        // do not release
	bool            doRebuild               = false;
	AbsoluteTime    my_abstime;
	UInt64          my_ns;
	SInt32          delta_secs;

	/* Only one auto rebuild per boot and only on boot from prelinked kernel */
	if (requestedPrelink || !sPrelinkBoot) {
		return;
	}

	/* no direct return from this point */
	IORecursiveLockLock(sKextLock);

	/* We need to wait for the IOKit daemon to get up and running with unloads already done
	 * and any new startup kexts loaded.
	 */
	if (!sConsiderUnloadsExecuted ||
	    !sDeferredLoadSucceeded) {
		goto finish;
	}

	/* we really only care about boot / system start up related kexts so bail
	 * if we're here after REBUILD_MAX_TIME.
	 */
	if (!_OSKextInPrelinkRebuildWindow()) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogArchiveFlag,
		    "%s prebuild rebuild has expired",
		    __FUNCTION__);
		requestedPrelink = true;
		goto finish;
	}

	/* we do not want to trigger a rebuild if we get here too close to waking
	 * up.  (see radar 10233768)
	 */
	IORecursiveLockLock(sKextInnerLock);

	clock_get_uptime(&my_abstime);
	delta_secs = MINIMUM_WAKEUP_SECONDS + 1;
	if (AbsoluteTime_to_scalar(&sLastWakeTime) != 0) {
		SUB_ABSOLUTETIME(&my_abstime, &sLastWakeTime);
		absolutetime_to_nanoseconds(my_abstime, &my_ns);
		delta_secs = (SInt32)(my_ns / NSEC_PER_SEC);
	}
	IORecursiveLockUnlock(sKextInnerLock);

	if (delta_secs < MINIMUM_WAKEUP_SECONDS) {
		/* too close to time of last wake from sleep */
		goto finish;
	}
	requestedPrelink = true;

	/* Now it's time to see if we have a reason to rebuild.  We may have done
	 * some loads and unloads but the kernel cache didn't actually change.
	 * We will rebuild if any kext is not marked prelinked AND is not in our
	 * list of prelinked kexts that got unloaded.  (see radar 9055303)
	 */
	kextIterator = OSCollectionIterator::withCollection(sKextsByID.get());
	if (!kextIterator) {
		goto finish;
	}

	while ((thisID = OSDynamicCast(OSSymbol, kextIterator->getNextObject()))) {
		OSKext *    thisKext;        // do not release

		thisKext = OSDynamicCast(OSKext, sKextsByID->getObject(thisID));
		if (!thisKext || thisKext->isPrelinked() || thisKext->isKernel()) {
			continue;
		}

		if (_OSKextInUnloadedPrelinkedKexts(thisKext->bundleID.get())) {
			continue;
		}
		/* kext is loaded and was not in current kernel cache so let's rebuild
		 */
		doRebuild = true;
		OSKextLog(/* kext */ NULL,
		    kOSKextLogArchiveFlag,
		    "considerRebuildOfPrelinkedKernel %s triggered rebuild",
		    thisKext->bundleID->getCStringNoCopy());
		break;
	}
	sUnloadedPrelinkedKexts->flushCollection();

	if (!doRebuild) {
		goto finish;
	}

	checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestPrelink,
	    prelinkRequest);
	if (checkResult != kOSReturnSuccess) {
		goto finish;
	}

	if (!sKernelRequests->setObject(prelinkRequest.get())) {
		goto finish;
	}

	OSKext::pingIOKitDaemon();

finish:
	IORecursiveLockUnlock(sKextLock);

	return;
}

#else /* !CONFIG_KXLD */

void
OSKext::considerRebuildOfPrelinkedKernel(void)
{
	/* in a non-dynamic kext loading world, there is never a reason to rebuild */
	return;
}

#endif /* CONFIG_KXLD */

#if PRAGMA_MARK
#pragma mark Dependencies
#endif
/*********************************************************************
*********************************************************************/
bool
OSKext::resolveDependencies(
	OSArray * loopStack)
{
	bool                   result                   = false;
	OSSharedPtr<OSArray>   localLoopStack;
	bool                   addedToLoopStack         = false;
	OSDictionary         * libraries                = NULL;        // do not release
	OSSharedPtr<OSCollectionIterator> libraryIterator;
	OSString             * libraryID                = NULL;        // do not release
	OSKext               * libraryKext              = NULL;        // do not release
	bool                   hasRawKernelDependency   = false;
	bool                   hasKernelDependency      = false;
	bool                   hasKPIDependency         = false;
	bool                   hasPrivateKPIDependency  = false;
	unsigned int           count;

#if CONFIG_KXLD
	OSString             * infoString               = NULL;        // do not release
	OSString             * readableString           = NULL;        // do not release
#endif // CONFIG_KXLD

	/* A kernel component will automatically have this flag set,
	 * and a loaded kext should also have it set (as should all its
	 * loaded dependencies).
	 */
	if (flags.hasAllDependencies) {
		result = true;
		goto finish;
	}

	/* Check for loops in the dependency graph.
	 */
	if (loopStack) {
		if (loopStack->getNextIndexOfObject(this, 0) != (unsigned int)-1) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s has a dependency loop; can't resolve dependencies.",
			    getIdentifierCString());
			goto finish;
		}
	} else {
		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogDependenciesFlag,
		    "Kext %s resolving dependencies.",
		    getIdentifierCString());

		localLoopStack = OSArray::withCapacity(6);         // any small capacity will do
		if (!localLoopStack) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s can't create bookkeeping stack to resolve dependencies.",
			    getIdentifierCString());
			goto finish;
		}
		loopStack = localLoopStack.get();
	}
	if (!loopStack->setObject(this)) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogDependenciesFlag,
		    "Kext %s - internal error resolving dependencies.",
		    getIdentifierCString());
		goto finish;
	}
	addedToLoopStack = true;

	/* Purge any existing kexts in the dependency list and start over.
	 */
	flushDependencies();
	if (dependencies) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogDependenciesFlag,
		    "Kext %s - internal error resolving dependencies.",
		    getIdentifierCString());
	}

	libraries = OSDynamicCast(OSDictionary,
	    getPropertyForHostArch(kOSBundleLibrariesKey));
	if (libraries == NULL || libraries->getCount() == 0) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
		    "Kext %s - can't resolve dependencies; %s missing/invalid type.",
		    getIdentifierCString(), kOSBundleLibrariesKey);
		goto finish;
	}

	/* Make a new array to hold the dependencies (flush freed the old one).
	 */
	dependencies = OSArray::withCapacity(libraries->getCount());
	if (!dependencies) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogDependenciesFlag,
		    "Kext %s - can't allocate dependencies array.",
		    getIdentifierCString());
		goto finish;
	}

	// xxx - compat: We used to add an implicit dependency on kernel 6.0
	// xxx - compat: if none were declared.

	libraryIterator = OSCollectionIterator::withCollection(libraries);
	if (!libraryIterator) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogDependenciesFlag,
		    "Kext %s - can't allocate dependencies iterator.",
		    getIdentifierCString());
		goto finish;
	}

	while ((libraryID = OSDynamicCast(OSString,
	    libraryIterator->getNextObject()))) {
		const char * library_id = libraryID->getCStringNoCopy();

		OSString * libraryVersion = OSDynamicCast(OSString,
		    libraries->getObject(libraryID));
		if (libraryVersion == NULL) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
			    "Kext %s - illegal type in OSBundleLibraries.",
			    getIdentifierCString());
			goto finish;
		}

		OSKextVersion libraryVers =
		    OSKextParseVersionString(libraryVersion->getCStringNoCopy());
		if (libraryVers == -1) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
			    "Kext %s - invalid library version %s.",
			    getIdentifierCString(),
			    libraryVersion->getCStringNoCopy());
			goto finish;
		}

		libraryKext = OSDynamicCast(OSKext, sKextsByID->getObject(libraryID));
		if (libraryKext == NULL) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s - library kext %s not found.",
			    getIdentifierCString(), library_id);
			goto finish;
		}

		if (!libraryKext->isCompatibleWithVersion(libraryVers)) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s - library kext %s not compatible "
			    "with requested version %s.",
			    getIdentifierCString(), library_id,
			    libraryVersion->getCStringNoCopy());
			goto finish;
		}

		/* If a nonprelinked library somehow got into the mix for a
		 * prelinked kext, at any point in the chain, we must fail
		 * because the prelinked relocs for the library will be all wrong.
		 */
		if (this->isPrelinked() &&
		    libraryKext->declaresExecutable() &&
		    !libraryKext->isPrelinked()) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s (prelinked) - library kext %s (v%s) not prelinked.",
			    getIdentifierCString(), library_id,
			    libraryVersion->getCStringNoCopy());
			goto finish;
		}

		if (!libraryKext->resolveDependencies(loopStack)) {
			goto finish;
		}

		/* Add the library directly only if it has an executable to link.
		 * Otherwise it's just used to collect other dependencies, so put
		 * *its* dependencies on the list for this kext.
		 */
		// xxx - We are losing info here; would like to make fake entries or
		// xxx - keep these in the dependency graph for loaded kexts.
		// xxx - I really want to make kernel components not a special case!
		if (libraryKext->declaresExecutable() ||
		    libraryKext->isInterface()) {
			if (dependencies->getNextIndexOfObject(libraryKext, 0) == (unsigned)-1) {
				dependencies->setObject(libraryKext);

				OSKextLog(this,
				    kOSKextLogDetailLevel |
				    kOSKextLogDependenciesFlag,
				    "Kext %s added dependency %s.",
				    getIdentifierCString(),
				    libraryKext->getIdentifierCString());
			}
		} else {
			int       numLibDependencies  = libraryKext->getNumDependencies();
			OSArray * libraryDependencies = libraryKext->getDependencies();
			int       index;

			if (numLibDependencies) {
				// xxx - this msg level should be 1 lower than the per-kext one
				OSKextLog(this,
				    kOSKextLogDetailLevel |
				    kOSKextLogDependenciesFlag,
				    "Kext %s pulling %d dependencies from codeless library %s.",
				    getIdentifierCString(),
				    numLibDependencies,
				    libraryKext->getIdentifierCString());
			}
			for (index = 0; index < numLibDependencies; index++) {
				OSKext * thisLibDependency = OSDynamicCast(OSKext,
				    libraryDependencies->getObject(index));
				if (dependencies->getNextIndexOfObject(thisLibDependency, 0) == (unsigned)-1) {
					dependencies->setObject(thisLibDependency);
					OSKextLog(this,
					    kOSKextLogDetailLevel |
					    kOSKextLogDependenciesFlag,
					    "Kext %s added dependency %s from codeless library %s.",
					    getIdentifierCString(),
					    thisLibDependency->getIdentifierCString(),
					    libraryKext->getIdentifierCString());
				}
			}
		}

		if ((strlen(library_id) == strlen(KERNEL_LIB)) &&
		    0 == strncmp(library_id, KERNEL_LIB, sizeof(KERNEL_LIB) - 1)) {
			hasRawKernelDependency = true;
		} else if (STRING_HAS_PREFIX(library_id, KERNEL_LIB_PREFIX)) {
			hasKernelDependency = true;
		} else if (STRING_HAS_PREFIX(library_id, KPI_LIB_PREFIX)) {
			hasKPIDependency = true;
			if (!strncmp(library_id, PRIVATE_KPI, sizeof(PRIVATE_KPI) - 1)) {
				hasPrivateKPIDependency = true;
			}
		}
	}

	if (hasRawKernelDependency) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
		    "Error - kext %s declares a dependency on %s, which is not permitted.",
		    getIdentifierCString(), KERNEL_LIB);
		goto finish;
	}
#if __LP64__
	if (hasKernelDependency) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogValidationFlag | kOSKextLogDependenciesFlag,
		    "Error - kext %s declares %s dependencies. "
		    "Only %s* dependencies are supported for 64-bit kexts.",
		    getIdentifierCString(), KERNEL_LIB, KPI_LIB_PREFIX);
		goto finish;
	}
	if (!hasKPIDependency) {
		OSKextLog(this,
		    kOSKextLogWarningLevel |
		    kOSKextLogDependenciesFlag,
		    "Warning - kext %s declares no %s* dependencies. "
		    "If it uses any KPIs, the link may fail with undefined symbols.",
		    getIdentifierCString(), KPI_LIB_PREFIX);
	}
#else /* __LP64__ */
	// xxx - will change to flatly disallow "kernel" dependencies at some point
	// xxx - is it invalid to do both "com.apple.kernel" and any
	// xxx - "com.apple.kernel.*"?

	if (hasKernelDependency && hasKPIDependency) {
		OSKextLog(this,
		    kOSKextLogWarningLevel |
		    kOSKextLogDependenciesFlag,
		    "Warning - kext %s has immediate dependencies on both "
		    "%s* and %s* components; use only one style.",
		    getIdentifierCString(), KERNEL_LIB, KPI_LIB_PREFIX);
	}

	if (!hasKernelDependency && !hasKPIDependency) {
		// xxx - do we want to use validation flag for these too?
		OSKextLog(this,
		    kOSKextLogWarningLevel |
		    kOSKextLogDependenciesFlag,
		    "Warning - %s declares no kernel dependencies; using %s.",
		    getIdentifierCString(), KERNEL6_LIB);
		OSKext * kernelKext = OSDynamicCast(OSKext,
		    sKextsByID->getObject(KERNEL6_LIB));
		if (kernelKext) {
			dependencies->setObject(kernelKext);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Error - Library %s not found for %s.",
			    KERNEL6_LIB, getIdentifierCString());
		}
	}

	/* If the kext doesn't have a raw kernel or KPI dependency, then add all of
	 * its indirect dependencies to simulate old-style linking.  XXX - Should
	 * check for duplicates.
	 */
	if (!hasKPIDependency) {
		unsigned int i;

		flags.hasBleedthrough = true;

		count = getNumDependencies();

		/* We add to the dependencies array in this loop, but do not iterate
		 * past its original count.
		 */
		for (i = 0; i < count; i++) {
			OSKext * dependencyKext = OSDynamicCast(OSKext,
			    dependencies->getObject(i));
			dependencyKext->addBleedthroughDependencies(dependencies.get());
		}
	}
#endif /* __LP64__ */

#if CONFIG_KXLD
	/*
	 * If we're not dynamically linking kexts, then we don't need to check
	 * copyright strings. The linker in user space has already done this.
	 */
	if (hasPrivateKPIDependency) {
		bool hasApplePrefix = false;
		bool infoCopyrightIsValid = false;
		bool readableCopyrightIsValid = false;

		hasApplePrefix = STRING_HAS_PREFIX(getIdentifierCString(),
		    APPLE_KEXT_PREFIX);

		infoString = OSDynamicCast(OSString,
		    getPropertyForHostArch("CFBundleGetInfoString"));
		if (infoString) {
			infoCopyrightIsValid =
			    kxld_validate_copyright_string(infoString->getCStringNoCopy());
		}

		readableString = OSDynamicCast(OSString,
		    getPropertyForHostArch("NSHumanReadableCopyright"));
		if (readableString) {
			readableCopyrightIsValid =
			    kxld_validate_copyright_string(readableString->getCStringNoCopy());
		}

		if (!hasApplePrefix || (!infoCopyrightIsValid && !readableCopyrightIsValid)) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Error - kext %s declares a dependency on %s. "
			    "Only Apple kexts may declare a dependency on %s.",
			    getIdentifierCString(), PRIVATE_KPI, PRIVATE_KPI);
			goto finish;
		}
	}
#endif // CONFIG_KXLD

	result = true;
	flags.hasAllDependencies = 1;

finish:

	if (addedToLoopStack) {
		count = loopStack->getCount();
		if (count > 0 && (this == loopStack->getObject(count - 1))) {
			loopStack->removeObject(count - 1);
		} else {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s - internal error resolving dependencies.",
			    getIdentifierCString());
		}
	}

	if (result && localLoopStack) {
		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogDependenciesFlag,
		    "Kext %s successfully resolved dependencies.",
		    getIdentifierCString());
	}

	return result;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::addBleedthroughDependencies(OSArray * anArray)
{
	bool result = false;
	unsigned int dependencyIndex, dependencyCount;

	dependencyCount = getNumDependencies();

	for (dependencyIndex = 0;
	    dependencyIndex < dependencyCount;
	    dependencyIndex++) {
		OSKext * dependency = OSDynamicCast(OSKext,
		    dependencies->getObject(dependencyIndex));
		if (!dependency) {
			OSKextLog(this,
			    kOSKextLogErrorLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s - internal error propagating compatibility dependencies.",
			    getIdentifierCString());
			goto finish;
		}
		if (anArray->getNextIndexOfObject(dependency, 0) == (unsigned int)-1) {
			anArray->setObject(dependency);
		}
		dependency->addBleedthroughDependencies(anArray);
	}

	result = true;

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
bool
OSKext::flushDependencies(bool forceFlag)
{
	bool result = false;

	/* Only clear the dependencies if the kext isn't loaded;
	 * we need the info for loaded kexts to track references.
	 */
	if (!isLoaded() || forceFlag) {
		if (dependencies) {
			// xxx - check level
			OSKextLog(this,
			    kOSKextLogProgressLevel |
			    kOSKextLogDependenciesFlag,
			    "Kext %s flushing dependencies.",
			    getIdentifierCString());
			dependencies.reset();
		}
		if (!isKernelComponent()) {
			flags.hasAllDependencies = 0;
		}
		result = true;
	}

	return result;
}

/*********************************************************************
*********************************************************************/
uint32_t
OSKext::getNumDependencies(void)
{
	if (!dependencies) {
		return 0;
	}
	return dependencies->getCount();
}

/*********************************************************************
*********************************************************************/
OSArray *
OSKext::getDependencies(void)
{
	return dependencies.get();
}

bool
OSKext::hasDependency(const OSSymbol * depID)
{
	bool result __block;

	if (depID == getIdentifier()) {
		return true;
	}
	if (!dependencies) {
		return false;
	}
	result = false;
	dependencies->iterateObjects(^bool (OSObject * obj) {
		OSKext * kext;
		kext = OSDynamicCast(OSKext, obj);
		if (!kext) {
		        return false;
		}
		result = (depID == kext->getIdentifier());
		return result;
	});
	return result;
}

#if PRAGMA_MARK
#pragma mark OSMetaClass Support
#endif
/*********************************************************************
*********************************************************************/
OSReturn
OSKext::addClass(
	OSMetaClass * aClass,
	uint32_t      numClasses)
{
	OSReturn result = kOSMetaClassNoInsKModSet;

	if (!metaClasses) {
		metaClasses = OSSet::withCapacity(numClasses);
		if (!metaClasses) {
			goto finish;
		}
	}

	if (metaClasses->containsObject(aClass)) {
		OSKextLog(this,
		    kOSKextLogWarningLevel |
		    kOSKextLogLoadFlag,
		    "Notice - kext %s has already registered class %s.",
		    getIdentifierCString(),
		    aClass->getClassName());
		result = kOSReturnSuccess;
		goto finish;
	}

	if (!metaClasses->setObject(aClass)) {
		goto finish;
	} else {
		OSKextLog(this,
		    kOSKextLogDetailLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s registered class %s.",
		    getIdentifierCString(),
		    aClass->getClassName());
	}

	if (!flags.autounloadEnabled) {
		const OSMetaClass * metaScan  = NULL;        // do not release

		for (metaScan = aClass; metaScan; metaScan = metaScan->getSuperClass()) {
			if (metaScan == OSTypeID(IOService)) {
				OSKextLog(this,
				    kOSKextLogProgressLevel |
				    kOSKextLogLoadFlag,
				    "Kext %s has IOService subclass %s; enabling autounload.",
				    getIdentifierCString(),
				    aClass->getClassName());

				flags.autounloadEnabled = (0 == flags.unloadUnsupported);
				break;
			}
		}
	}

	notifyAddClassObservers(this, aClass, flags);

	result = kOSReturnSuccess;

finish:
	if (result != kOSReturnSuccess) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s failed to register class %s.",
		    getIdentifierCString(),
		    aClass->getClassName());
	}

	return result;
}

/*********************************************************************
*********************************************************************/
OSReturn
OSKext::removeClass(
	OSMetaClass * aClass)
{
	OSReturn result = kOSMetaClassNoKModSet;

	if (!metaClasses) {
		goto finish;
	}

	if (!metaClasses->containsObject(aClass)) {
		OSKextLog(this,
		    kOSKextLogWarningLevel |
		    kOSKextLogLoadFlag,
		    "Notice - kext %s asked to unregister unknown class %s.",
		    getIdentifierCString(),
		    aClass->getClassName());
		result = kOSReturnSuccess;
		goto finish;
	}

	OSKextLog(this,
	    kOSKextLogDetailLevel |
	    kOSKextLogLoadFlag,
	    "Kext %s unregistering class %s.",
	    getIdentifierCString(),
	    aClass->getClassName());

	metaClasses->removeObject(aClass);

	notifyRemoveClassObservers(this, aClass, flags);

	result = kOSReturnSuccess;

finish:
	if (result != kOSReturnSuccess) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Failed to unregister kext %s class %s.",
		    getIdentifierCString(),
		    aClass->getClassName());
	}
	return result;
}

/*********************************************************************
*********************************************************************/
OSSet *
OSKext::getMetaClasses(void)
{
	return metaClasses.get();
}

/*********************************************************************
*********************************************************************/
bool
OSKext::hasOSMetaClassInstances(void)
{
	bool                   result        = false;
	OSSharedPtr<OSCollectionIterator> classIterator;
	OSMetaClass          * checkClass    = NULL;        // do not release

	if (!metaClasses) {
		goto finish;
	}

	classIterator = OSCollectionIterator::withCollection(metaClasses.get());
	if (!classIterator) {
		// xxx - log alloc failure?
		goto finish;
	}
	while ((checkClass = (OSMetaClass *)classIterator->getNextObject())) {
		if (checkClass->getInstanceCount()) {
			result = true;
			goto finish;
		}
	}

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::reportOSMetaClassInstances(
	const char     * kextIdentifier,
	OSKextLogSpec    msgLogSpec)
{
	OSSharedPtr<OSKext> theKext;

	theKext = OSKext::lookupKextWithIdentifier(kextIdentifier);
	if (!theKext) {
		goto finish;
	}

	theKext->reportOSMetaClassInstances(msgLogSpec);
finish:
	return;
}

/*********************************************************************
*********************************************************************/
void
OSKext::reportOSMetaClassInstances(OSKextLogSpec msgLogSpec)
{
	OSSharedPtr<OSCollectionIterator> classIterator;
	OSMetaClass          * checkClass    = NULL;        // do not release

	if (!metaClasses) {
		goto finish;
	}

	classIterator = OSCollectionIterator::withCollection(metaClasses.get());
	if (!classIterator) {
		goto finish;
	}
	while ((checkClass = (OSMetaClass *)classIterator->getNextObject())) {
		if (checkClass->getInstanceCount()) {
			OSKextLog(this,
			    msgLogSpec,
			    "    Kext %s class %s has %d instance%s.",
			    getIdentifierCString(),
			    checkClass->getClassName(),
			    checkClass->getInstanceCount(),
			    checkClass->getInstanceCount() == 1 ? "" : "s");
		}
	}

finish:
	return;
}

#if PRAGMA_MARK
#pragma mark User-Space Requests
#endif

static kern_return_t
patchDextLaunchRequests(task_t calling_task, OSArray *requests)
{
	OSReturn result = kOSReturnSuccess;
	for (uint32_t requestIndex = 0; requestIndex < requests->getCount(); requestIndex++) {
		OSDictionary * request = NULL;         //do not release
		IOUserServerCheckInToken * token = NULL;         //do not release
		OSString * requestPredicate = NULL;         //do not release
		OSSharedPtr<OSNumber> portNameNumber;
		mach_port_name_t portName = 0;
		request = OSDynamicCast(OSDictionary, requests->getObject(requestIndex));
		if (!request) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
			    "Elements of request should be of type OSDictionary");
			result = kOSKextReturnInternalError;
			goto finish;
		}
		requestPredicate = _OSKextGetRequestPredicate(request);
		if (!requestPredicate) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
			    "Failed to get request predicate");
			result = kOSKextReturnInternalError;
			goto finish;
		}
		// is this a dext launch?
		if (requestPredicate->isEqualTo(kKextRequestPredicateRequestDaemonLaunch)) {
			token = OSDynamicCast(IOUserServerCheckInToken, _OSKextGetRequestArgument(request, kKextRequestArgumentCheckInToken));
			if (!token) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
				    "Could not find a IOUserServerCheckInToken in daemon launch request.");
				result = kOSKextReturnInternalError;
				goto finish;
			}
			portName = iokit_make_send_right(calling_task, token, IKOT_IOKIT_IDENT);
			if (portName == 0 || portName == MACH_PORT_DEAD) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
				    "Could not create send right for object.");
				result = kOSKextReturnInternalError;
				goto finish;
			}
			// Store the mach port name as a OSNumber
			portNameNumber = OSNumber::withNumber(portName, CHAR_BIT * sizeof(portName));
			if (!portNameNumber) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
				    "Could not create OSNumber object.");
				result = kOSKextReturnNoMemory;
				goto finish;
			}
			if (!_OSKextSetRequestArgument(request, kKextRequestArgumentCheckInToken, portNameNumber.get())) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
				    "Could not set OSNumber object as request " kKextRequestArgumentCheckInToken);
				result = kOSKextReturnNoMemory;
				goto finish;
			}
		}
finish:
		if (result != kOSReturnSuccess) {
			break;
		}
	}
	return result;
}

bool
OSKext::iokitDaemonActive()
{
	bool result;
	IORecursiveLockLock(sKextLock);
	result = sIOKitDaemonActive && !sOSKextWasResetAfterUserspaceReboot;
	IORecursiveLockUnlock(sKextLock);
	return result;
}

/*********************************************************************
* XXX - this function is a big ugly mess
*********************************************************************/
/* static */
OSReturn
OSKext::handleRequest(
	host_priv_t     hostPriv,
	OSKextLogSpec   clientLogFilter,
	char          * requestBuffer,
	uint32_t        requestLength,
	char         ** responseOut,
	uint32_t      * responseLengthOut,
	char         ** logInfoOut,
	uint32_t      * logInfoLengthOut)
{
	OSReturn       result             = kOSReturnError;
	kern_return_t  kmem_result        = KERN_FAILURE;

	char         * response           = NULL;        // returned by reference
	uint32_t       responseLength     = 0;

	bool           taskCanManageAllKCs   = false;
	bool           taskOnlyManagesBootKC = false;

	OSSharedPtr<OSObject>     parsedXML;
	OSDictionary            * requestDict    = NULL;        // do not release
	OSSharedPtr<OSString>     errorString;

	OSSharedPtr<OSObject>     responseObject;

	OSSharedPtr<OSSerialize>  serializer;

	OSSharedPtr<OSArray>      logInfoArray;

	OSString     * predicate          = NULL;        // do not release
	OSString     * kextIdentifier     = NULL;        // do not release
	OSArray      * kextIdentifiers    = NULL;        // do not release
	OSKext       * theKext            = NULL;        // do not release
	OSBoolean    * boolArg            = NULL;        // do not release


	IORecursiveLockLock(sKextLock);

	if (responseOut) {
		*responseOut = NULL;
		*responseLengthOut = 0;
	}
	if (logInfoOut) {
		*logInfoOut = NULL;
		*logInfoLengthOut = 0;
	}

	OSKext::setUserSpaceLogFilter(clientLogFilter, logInfoOut ? true : false);

	/* XML must be nul-terminated.
	 */
	if (requestBuffer[requestLength - 1] != '\0') {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Invalid request from user space (not nul-terminated).");
		result = kOSKextReturnBadData;
		goto finish;
	}
	parsedXML = OSUnserializeXML((const char *)requestBuffer, errorString);
	if (parsedXML) {
		requestDict = OSDynamicCast(OSDictionary, parsedXML.get());
	}
	if (!requestDict) {
		const char * errorCString = "(unknown error)";

		if (errorString && errorString->getCStringNoCopy()) {
			errorCString = errorString->getCStringNoCopy();
		} else if (parsedXML) {
			errorCString = "not a dictionary";
		}
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Error unserializing request from user space: %s.",
		    errorCString);
		result = kOSKextReturnSerialization;
		goto finish;
	}

	predicate = _OSKextGetRequestPredicate(requestDict);
	if (!predicate) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Recieved kext request from user space with no predicate.");
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}
	OSKextLog(/* kext */ NULL,
	    kOSKextLogDebugLevel |
	    kOSKextLogIPCFlag,
	    "Received '%s' request from user space.",
	    predicate->getCStringNoCopy());

	/*
	 * All management of file sets requires an entitlement
	 */
	result = kOSKextReturnNotPrivileged;
	if (predicate->isEqualTo(kKextRequestPredicateUnload) ||
	    predicate->isEqualTo(kKextRequestPredicateStart) ||
	    predicate->isEqualTo(kKextRequestPredicateStop) ||
	    predicate->isEqualTo(kKextRequestPredicateGetKernelRequests) ||
	    predicate->isEqualTo(kKextRequestPredicateSendResource) ||
	    predicate->isEqualTo(kKextRequestPredicateLoadFileSetKC) ||
	    predicate->isEqualTo(kKextRequestPredicateLoadCodeless) ||
	    predicate->isEqualTo(kKextRequestPredicateLoadFromKC) ||
	    predicate->isEqualTo(kKextRequestPredicateMissingAuxKCBundles) ||
	    predicate->isEqualTo(kKextRequestPredicateAuxKCBundleAvailable) ||
	    predicate->isEqualTo(kKextRequestPredicateDaemonReady)) {
		if (!iokitDaemonAvailable()) {
			panic("Received unexpected request in environment where " kIOKitDaemonName " is unavailable");
		}
		if (hostPriv == HOST_PRIV_NULL) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Access Failure - must be root user.");
			goto finish;
		}
		taskCanManageAllKCs = IOCurrentTaskHasEntitlement(kOSKextCollectionManagementEntitlement) == TRUE;
		taskOnlyManagesBootKC = IOCurrentTaskHasEntitlement(kOSKextOnlyBootKCManagementEntitlement) == TRUE;

		if (!taskCanManageAllKCs && !taskOnlyManagesBootKC) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Access Failure - client not entitled to manage file sets.");
			goto finish;
		}

		/*
		 * The OnlyBootKC entitlement restricts the
		 * collection-management entitlement to only managing kexts in
		 * the BootKC. All other predicates that alter global state or
		 * add new KCs are disallowed.
		 */
		if (taskOnlyManagesBootKC &&
		    (predicate->isEqualTo(kKextRequestPredicateGetKernelRequests) ||
		    predicate->isEqualTo(kKextRequestPredicateSendResource) ||
		    predicate->isEqualTo(kKextRequestPredicateLoadFileSetKC) ||
		    predicate->isEqualTo(kKextRequestPredicateLoadCodeless) ||
		    predicate->isEqualTo(kKextRequestPredicateMissingAuxKCBundles) ||
		    predicate->isEqualTo(kKextRequestPredicateAuxKCBundleAvailable) ||
		    predicate->isEqualTo(kKextRequestPredicateDaemonReady))) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Access Failure - client not entitled to manage non-primary KCs");
			goto finish;
		}

		/*
		 * If we get here, then the process either has the full KC
		 * management entitlement, or it has the BootKC-only
		 * entitlement and the request is about the BootKC.
		 */
	}

	/* Get common args in anticipation of use.
	 */
	kextIdentifier = OSDynamicCast(OSString, _OSKextGetRequestArgument(
		    requestDict, kKextRequestArgumentBundleIdentifierKey));
	kextIdentifiers = OSDynamicCast(OSArray, _OSKextGetRequestArgument(
		    requestDict, kKextRequestArgumentBundleIdentifierKey));
	if (kextIdentifier) {
		theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier));
	}
	boolArg = OSDynamicCast(OSBoolean, _OSKextGetRequestArgument(
		    requestDict, kKextRequestArgumentValueKey));

	if (taskOnlyManagesBootKC &&
	    theKext &&
	    theKext->isInFileset() &&
	    theKext->kc_type != KCKindPrimary) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Access Failure - client not entitled to manage kext in non-primary KC");
		result = kOSKextReturnNotPrivileged;
		goto finish;
	}
	result = kOSKextReturnInvalidArgument;

	if (predicate->isEqualTo(kKextRequestPredicateStart)) {
		if (!kextIdentifier) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to kext start request.");
		} else if (!theKext) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Kext %s not found for start request.",
			    kextIdentifier->getCStringNoCopy());
			result = kOSKextReturnNotFound;
		} else {
			result = theKext->start();
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateStop)) {
		if (!kextIdentifier) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to kext stop request.");
		} else if (!theKext) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Kext %s not found for stop request.",
			    kextIdentifier->getCStringNoCopy());
			result = kOSKextReturnNotFound;
		} else {
			result = theKext->stop();
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateMissingAuxKCBundles)) {
		result = OSKext::setMissingAuxKCBundles(requestDict);
	} else if (predicate->isEqualTo(kKextRequestPredicateAuxKCBundleAvailable)) {
		if (!kextIdentifier) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to AuxKC Bundle Available request.");
		} else {
			result = OSKext::setAuxKCBundleAvailable(kextIdentifier, requestDict);
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateLoadFromKC)) {
		if (!kextIdentifier) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to kext load from KC request.");
		} else if (!theKext) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Kext %s not found for load from KC request.",
			    kextIdentifier->getCStringNoCopy());
			result = kOSKextReturnNotFound;
		} else if (!theKext->isInFileset()) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Kext %s does not exist in a KC: refusing to load.",
			    kextIdentifier->getCStringNoCopy());
			result = kOSKextReturnNotLoadable;
		} else {
			result = OSKext::loadKextFromKC(theKext, requestDict);
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateLoadCodeless)) {
		if (!kextIdentifier) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to codeless kext load interface (missing identifier).");
		} else {
			result = OSKext::loadCodelessKext(kextIdentifier, requestDict);
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateUnload)) {
		if (!kextIdentifier) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to kext unload request.");
		} else if (!theKext) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Kext %s not found for unload request.",
			    kextIdentifier->getCStringNoCopy());
			result = kOSKextReturnNotFound;
		} else {
			if (theKext->isDriverKit()) {
				result = OSKext::removeDext(theKext);
			} else {
				OSBoolean * terminateFlag = OSDynamicCast(OSBoolean,
				    _OSKextGetRequestArgument(requestDict,
				    kKextRequestArgumentTerminateIOServicesKey));
				result = OSKext::removeKext(theKext, terminateFlag == kOSBooleanTrue);
			}
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateSendResource)) {
		result = OSKext::dispatchResource(requestDict);
	} else if (predicate->isEqualTo(kKextRequestPredicateGetUUIDByAddress)) {
		OSNumber     *lookupNum   = NULL;
		lookupNum = OSDynamicCast(OSNumber,
		    _OSKextGetRequestArgument(requestDict,
		    kKextRequestArgumentLookupAddressKey));

		responseObject = OSKext::copyKextUUIDForAddress(lookupNum);
		if (responseObject) {
			result = kOSReturnSuccess;
		} else {
			goto finish;
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateGetLoaded) ||
	    predicate->isEqualTo(kKextRequestPredicateGetLoadedByUUID) ||
	    predicate->isEqualTo(kKextRequestPredicateGetKextsInCollection) ||
	    predicate->isEqualTo(kKextRequestPredicateGetDexts)) {
		OSBoolean    * delayAutounloadBool = NULL;
		OSObject     * infoKeysRaw         = NULL;
		OSArray      * infoKeys            = NULL;
		uint32_t       infoKeysCount       = 0;

		delayAutounloadBool = OSDynamicCast(OSBoolean,
		    _OSKextGetRequestArgument(requestDict,
		    kKextRequestArgumentDelayAutounloadKey));

		/* If asked to delay autounload, reset the timer if it's currently set.
		 * (That is, don't schedule an unload if one isn't already pending.
		 */
		if (delayAutounloadBool == kOSBooleanTrue) {
			OSKext::considerUnloads(/* rescheduleOnly? */ true);
		}

		infoKeysRaw = _OSKextGetRequestArgument(requestDict,
		    kKextRequestArgumentInfoKeysKey);
		infoKeys = OSDynamicCast(OSArray, infoKeysRaw);
		if (infoKeysRaw && !infoKeys) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid arguments to kext info request.");
			goto finish;
		}

		if (infoKeys) {
			infoKeysCount = infoKeys->getCount();
			for (uint32_t i = 0; i < infoKeysCount; i++) {
				if (!OSDynamicCast(OSString, infoKeys->getObject(i))) {
					OSKextLog(/* kext */ NULL,
					    kOSKextLogErrorLevel |
					    kOSKextLogIPCFlag,
					    "Invalid arguments to kext info request.");
					goto finish;
				}
			}
		}

		if (predicate->isEqualTo(kKextRequestPredicateGetLoaded)) {
			responseObject = OSKext::copyLoadedKextInfo(kextIdentifiers, infoKeys);
		} else if (predicate->isEqualTo(kKextRequestPredicateGetLoadedByUUID)) {
			responseObject = OSKext::copyLoadedKextInfoByUUID(kextIdentifiers, infoKeys);
		} else if (predicate->isEqualTo(kKextRequestPredicateGetKextsInCollection)) {
			responseObject = OSKext::copyKextCollectionInfo(requestDict, infoKeys);
		} else if (predicate->isEqualTo(kKextRequestPredicateGetDexts)) {
			responseObject = OSKext::copyDextsInfo(kextIdentifiers, infoKeys);
		}

		if (!responseObject) {
			result = kOSKextReturnInternalError;
		} else {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogDebugLevel |
			    kOSKextLogIPCFlag,
			    "Returning loaded kext info.");
			result = kOSReturnSuccess;
		}
	} else if (predicate->isEqualTo(kKextRequestPredicateGetKernelRequests)) {
		/* Hand the current sKernelRequests array to the caller
		 * (who must release it), and make a new one.
		 */
		responseObject = os::move(sKernelRequests);
		sKernelRequests = OSArray::withCapacity(0);
		sPostedKextLoadIdentifiers->flushCollection();
		OSKextLog(/* kext */ NULL,
		    kOSKextLogDebugLevel |
		    kOSKextLogIPCFlag,
		    "Returning kernel requests.");
		result = kOSReturnSuccess;
	} else if (predicate->isEqualTo(kKextRequestPredicateGetAllLoadRequests)) {
		/* Return the set of all requested bundle identifiers */
		responseObject = sAllKextLoadIdentifiers;
		OSKextLog(/* kext */ NULL,
		    kOSKextLogDebugLevel |
		    kOSKextLogIPCFlag,
		    "Returning load requests.");
		result = kOSReturnSuccess;
	} else if (predicate->isEqualTo(kKextRequestPredicateLoadFileSetKC)) {
		printf("KextLog: Loading FileSet KC(s)\n");
		result = OSKext::loadFileSetKexts(requestDict);
	} else if (predicate->isEqualTo(kKextRequestPredicateDaemonReady)) {
		bool active = iokitDaemonActive();
		printf("KextLog: " kIOKitDaemonName " is %s\n", active ? "active" : "not active");
		if (sOSKextWasResetAfterUserspaceReboot) {
			printf("KextLog: was reset after userspace reboot\n");
		}
		result = active ? kOSReturnSuccess : kIOReturnNotReady;
	} else {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogDebugLevel |
		    kOSKextLogIPCFlag,
		    "Received '%s' invalid request from user space.",
		    predicate->getCStringNoCopy());
		goto finish;
	}

	/**********
	 * Now we have handle the request, or not. Gather up the response & logging
	 * info to ship to user space.
	 *********/

	/* Note: Nothing in OSKext is supposed to retain requestDict,
	 * but you never know....
	 */
	if (requestDict->getRetainCount() > 1) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogWarningLevel |
		    kOSKextLogIPCFlag,
		    "Request from user space still retained by a kext; "
		    "probable memory leak.");
	}

	if (responseOut && responseObject) {
		serializer = OSSerialize::withCapacity(0);
		if (!serializer) {
			result = kOSKextReturnNoMemory;
			goto finish;
		}
		/*
		 * Before serializing the kernel requests, patch the dext launch requests so
		 * that the value for kKextRequestArgumentCheckInToken is a mach port name for the
		 * IOUserServerCheckInToken kernel object.
		 */
		if (predicate->isEqualTo(kKextRequestPredicateGetKernelRequests)) {
			OSArray * requests = OSDynamicCast(OSArray, responseObject.get());
			task_t calling_task = current_task();
			if (!requests) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
				    "responseObject should be an OSArray if predicate is " kKextRequestPredicateGetKernelRequests);
				result = kOSKextReturnInternalError;
				goto finish;
			}
			result = patchDextLaunchRequests(calling_task, requests);
			if (result != kOSReturnSuccess) {
				OSKextLog(/* kext */ NULL,
				    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
				    "Failed to patch dext launch requests.");
				goto finish;
			}
		}

		if (!responseObject->serialize(serializer.get())) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogGeneralFlag | kOSKextLogErrorLevel,
			    "Failed to serialize response to request from user space.");
			result = kOSKextReturnSerialization;
			goto finish;
		}

		response = (char *)serializer->text();
		responseLength = serializer->getLength();
	}

	if (responseOut && response) {
		char * buffer;

		/* This kmem_alloc sets the return value of the function.
		 */
		kmem_result = kmem_alloc(kernel_map, (vm_offset_t *)&buffer,
		    round_page(responseLength), KMA_DATA_SHARED, VM_KERN_MEMORY_OSKEXT);
		if (kmem_result != KERN_SUCCESS) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Failed to copy response to request from user space.");
			result = kmem_result;
			goto finish;
		} else {
			/* 11981737 - clear uninitialized data in last page */
			bzero((void *)(buffer + responseLength),
			    (round_page(responseLength) - responseLength));
			memcpy(buffer, response, responseLength);
			*responseOut = buffer;
			*responseLengthOut = responseLength;
		}
	}

finish:

	/* Gather up the collected log messages for user space. Any messages
	 * messages past this call will not make it up as log messages but
	 * will be in the system log. Note that we ignore the return of the
	 * serialize; it has no bearing on the operation at hand even if we
	 * fail to get the log messages.
	 */
	logInfoArray = OSKext::clearUserSpaceLogFilter();

	if (logInfoArray && logInfoOut && logInfoLengthOut) {
		(void)OSKext::serializeLogInfo(logInfoArray.get(),
		    logInfoOut, logInfoLengthOut);
	}

	IORecursiveLockUnlock(sKextLock);

	return result;
}

#if PRAGMA_MARK
#pragma mark Linked Kext Collection Support
#endif

static int
__whereIsAddr(vm_offset_t theAddr, unsigned long *segSizes, vm_offset_t *segAddrs, int segCount)
{
	for (int i = 0; i < segCount; i++) {
		vm_offset_t segStart = segAddrs[i];
		vm_offset_t segEnd = segStart + (vm_offset_t)segSizes[i];

		if (theAddr >= segStart && theAddr < segEnd) {
			return i;
		}
	}
	return -1;
}

static void
__slideOldKaslrOffsets(kernel_mach_header_t *mh,
    kernel_segment_command_t *kextTextSeg,
    OSData *kaslrOffsets)
{
	static const char *plk_segNames[] = {
		"__TEXT",
		"__TEXT_EXEC",
		"__DATA",
		"__DATA_CONST",
		"__LINKEDIT",
		"__PRELINK_TEXT",
		"__PLK_TEXT_EXEC",
		"__PRELINK_DATA",
		"__PLK_DATA_CONST",
		"__PLK_LLVM_COV",
		"__PLK_LINKEDIT",
		"__PRELINK_INFO"
	};
	static const size_t num_plk_seg = (size_t)(sizeof(plk_segNames) / sizeof(plk_segNames[0]));

	unsigned long plk_segSizes[num_plk_seg];
	vm_offset_t   plk_segAddrs[num_plk_seg];

	for (size_t i = 0; i < num_plk_seg; i++) {
		plk_segSizes[i] = 0;
		plk_segAddrs[i] = (vm_offset_t)getsegdatafromheader(mh, plk_segNames[i], &plk_segSizes[i]);
	}

	uint64_t kextTextStart = (uint64_t)kextTextSeg->vmaddr;

	int slidKextAddrCount = 0;
	int badSlideAddr = 0;
	int badSlideTarget = 0;

	struct kaslrPackedOffsets {
		uint32_t    count;          /* number of offsets */
		uint32_t    offsetsArray[];        /* offsets to slide */
	};
	const struct kaslrPackedOffsets *myOffsets = NULL;
	myOffsets = (const struct kaslrPackedOffsets *)kaslrOffsets->getBytesNoCopy();

	for (uint32_t j = 0; j < myOffsets->count; j++) {
		uint64_t   slideOffset = (uint64_t)myOffsets->offsetsArray[j];
		vm_offset_t *slideAddr = (vm_offset_t *)((uint64_t)kextTextStart + slideOffset);
		int        slideAddrSegIndex = -1;
		int        addrToSlideSegIndex = -1;

		slideAddrSegIndex = __whereIsAddr((vm_offset_t)slideAddr, &plk_segSizes[0], &plk_segAddrs[0], num_plk_seg);
		if (slideAddrSegIndex >= 0) {
			addrToSlideSegIndex = __whereIsAddr(ml_static_slide(*slideAddr), &plk_segSizes[0], &plk_segAddrs[0], num_plk_seg);
			if (addrToSlideSegIndex < 0) {
				badSlideTarget++;
				continue;
			}
		} else {
			badSlideAddr++;
			continue;
		}

		slidKextAddrCount++;
		*slideAddr = ml_static_slide(*slideAddr);
	}         // for ...
}



/********************************************************************
* addKextsFromKextCollection
*
* Input: MachO header of kext collection. The MachO is assumed to
*        have a section named 'info_seg_name,info_sect_name' that
*        contains a serialized XML info dictionary. This dictionary
*        contains a UUID, possibly a set of relocations (for older
*        kxld-built binaries), and an array of kext personalities.
*
********************************************************************/
bool
OSKext::addKextsFromKextCollection(kernel_mach_header_t *mh,
    OSDictionary *infoDict, const char *text_seg_name,
    OSData **kcUUID, kc_kind_t type)
{
	bool result = false;

	OSArray *kextArray     = NULL;        // do not release
	OSData *infoDictKCUUID = NULL;         // do not release
	OSData *kaslrOffsets   = NULL;        // do not release

	IORegistryEntry *registryRoot = NULL;         // do not release
	OSSharedPtr<OSNumber> kcKextCount;

	/* extract the KC UUID from the dictionary */
	infoDictKCUUID = OSDynamicCast(OSData, infoDict->getObject(kPrelinkInfoKCIDKey));
	if (infoDictKCUUID) {
		if (infoDictKCUUID->getLength() != sizeof(uuid_t)) {
			panic("kcUUID length is %d, expected %lu",
			    infoDictKCUUID->getLength(), sizeof(uuid_t));
		}
	}

	/* locate the array of kext dictionaries */
	kextArray = OSDynamicCast(OSArray, infoDict->getObject(kPrelinkInfoDictionaryKey));
	if (!kextArray) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "The given KC has no kext info dictionaries");
		goto finish;
	}

	/*
	 * old-style KASLR offsets may be present in the info dictionary. If
	 * we find them, use them and eventually slide them.
	 */
	kaslrOffsets = OSDynamicCast(OSData, infoDict->getObject(kPrelinkLinkKASLROffsetsKey));

	/*
	 * Before processing any kexts, locate the special kext bundle which
	 * contains a list of kexts that we are to prevent from loading.
	 */
	createExcludeListFromPrelinkInfo(kextArray);

	/*
	 * Create OSKext objects for each kext we find in the array of kext
	 * info plist dictionaries.
	 */
	for (int i = 0; i < (int)kextArray->getCount(); ++i) {
		OSDictionary *kextDict = NULL;
		kextDict = OSDynamicCast(OSDictionary, kextArray->getObject(i));
		if (!kextDict) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag,
			    "Kext info dictionary for kext #%d isn't a dictionary?", i);
			continue;
		}

		/*
		 * Create the kext for the entry, then release it, because the
		 * kext system keeps a reference around until the kext is
		 * explicitly removed.  Any creation/registration failures are
		 * already logged for us.
		 */
		withPrelinkedInfoDict(kextDict, (kaslrOffsets ? TRUE : FALSE), type);
	}

	/*
	 * slide old-style kxld relocations
	 * NOTE: this is still used on embedded KCs built with kcgen
	 * TODO: Remove this once we use the new kext linker everywhere!
	 */
	if (kaslrOffsets && vm_kernel_slide > 0) {
		kernel_segment_command_t *text_segment = NULL;
		text_segment = getsegbynamefromheader(mh, text_seg_name);
		if (!text_segment) {
			OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
			    "Can't find a TEXT segment named '%s' in macho header", text_seg_name);
			goto finish;
		}

		__slideOldKaslrOffsets(mh, text_segment, kaslrOffsets);
		/* All kexts covered by the old-style kaslr relocation list are now slid, set VM protections for them */
		setAllVMAttributes();
	}

	/* Store the number of prelinked kexts in the registry so we can tell
	 * when the system has been started from a prelinked kernel.
	 */
	registryRoot = IORegistryEntry::getRegistryRoot();
	assert(registryRoot);

	kcKextCount = OSNumber::withNumber((unsigned long long)infoDict->getCount(), 8 * sizeof(uint32_t));
	assert(kcKextCount);
	if (kcKextCount) {
		OSSharedPtr<OSObject> prop = registryRoot->copyProperty(kOSPrelinkKextCountKey);
		OSNumber *num;
		num = OSDynamicCast(OSNumber, prop.get());
		if (num) {
			kcKextCount->addValue(num->unsigned64BitValue());
		}
		registryRoot->setProperty(kOSPrelinkKextCountKey, kcKextCount.get());
	}

	OSKextLog(/* kext */ NULL,
	    kOSKextLogProgressLevel |
	    kOSKextLogGeneralFlag | kOSKextLogKextBookkeepingFlag |
	    kOSKextLogDirectoryScanFlag | kOSKextLogArchiveFlag,
	    "%u prelinked kexts", infoDict->getCount());


	if (kcUUID && infoDictKCUUID) {
		*kcUUID = OSData::withData(infoDictKCUUID).detach();
	}

	result = true;

finish:
	return result;
}

bool
OSKext::addKextsFromKextCollection(kernel_mach_header_t *mh,
    OSDictionary *infoDict, const char *text_seg_name,
    OSSharedPtr<OSData> &kcUUID, kc_kind_t type)
{
	OSData  *result = NULL;
	bool success = addKextsFromKextCollection(mh,
	    infoDict,
	    text_seg_name,
	    &result,
	    type);
	if (success) {
		kcUUID.reset(result, OSNoRetain);
	}
	return success;
}

static OSSharedPtr<OSObject> deferredAuxKCXML;
bool
OSKext::registerDeferredKextCollection(kernel_mach_header_t *mh,
    OSSharedPtr<OSObject> &parsedXML, kc_kind_t type)
{
	if (type != KCKindAuxiliary) {
		return false;
	}

	kernel_mach_header_t *_mh;
	_mh = (kernel_mach_header_t*)PE_get_kc_header(type);
	if (!_mh || _mh != mh) {
		return false;
	}

	if (deferredAuxKCXML) {
		/* only allow this to be called once */
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "An Aux KC has already been registered for deferred processing.");
		return false;
	}

	OSDictionary *infoDict = OSDynamicCast(OSDictionary, parsedXML.get());
	if (!infoDict) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "The Aux KC has info dictionary");
		return false;
	}

	OSData *kcUUID = OSDynamicCast(OSData, infoDict->getObject(kPrelinkInfoKCIDKey));
	if (!kcUUID || kcUUID->getLength() != sizeof(uuid_t)) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "The Aux KC has no UUID in %s", kPrelinkInfoKCIDKey);
		return false;
	}

	/*
	 * Copy the AuxKC UUID to make sure that the kern.auxiliaryfilesetuuid
	 * sysctl can return the UUID to user space which will check this
	 * value for errors.
	 */
	memcpy((void *)&auxkc_uuid, (const void *)kcUUID->getBytesNoCopy(),
	    kcUUID->getLength());
	uuid_unparse_upper(auxkc_uuid, auxkc_uuid_string);
	auxkc_uuid_valid = TRUE;

	deferredAuxKCXML = parsedXML;

	return true;
}

OSSharedPtr<OSObject>
OSKext::consumeDeferredKextCollection(kc_kind_t type)
{
	if (type != KCKindAuxiliary || !deferredAuxKCXML) {
		return NULL;
	}

	return os::move(deferredAuxKCXML);
}

#if PRAGMA_MARK
#pragma mark Profile-Guided-Optimization Support
#endif

// #include <InstrProfiling.h>
extern "C" {
uint64_t __llvm_profile_get_size_for_buffer_internal(
	const char *DataBegin,
	const char *DataEnd,
	const char *CountersBegin,
	const char *CountersEnd,
	const char *BitmapBegin,
	const char *BitmapEnd,
	const char *NamesBegin,
	const char *NamesEnd,
	const char *VTableBegin,
	const char *VTableEnd,
	const char *VNamesBegin,
	const char *VNamesEnd);
int __llvm_profile_write_buffer_internal(
	char *Buffer,
	const char *DataBegin,
	const char *DataEnd,
	const char *CountersBegin,
	const char *CountersEnd,
	const char *BitmapBegin,
	const char *BitmapEnd,
	const char *NamesBegin,
	const char *NamesEnd);
}

static
void
OSKextPgoMetadataPut(char *pBuffer,
    size_t *position,
    size_t bufferSize,
    uint32_t *num_pairs,
    const char *key,
    const char *value)
{
	size_t strlen_key = strlen(key);
	size_t strlen_value = strlen(value);
	size_t len = strlen(key) + 1 + strlen(value) + 1;
	char *pos = pBuffer + *position;
	*position += len;
	if (pBuffer && bufferSize && *position <= bufferSize) {
		memcpy(pos, key, strlen_key); pos += strlen_key;
		*(pos++) = '=';
		memcpy(pos, value, strlen_value); pos += strlen_value;
		*(pos++) = 0;
		if (num_pairs) {
			(*num_pairs)++;
		}
	}
}


static
void
OSKextPgoMetadataPutMax(size_t *position, const char *key, size_t value_max)
{
	*position += strlen(key) + 1 + value_max + 1;
}


static
void
OSKextPgoMetadataPutAll(OSKext *kext,
    uuid_t instance_uuid,
    char *pBuffer,
    size_t *position,
    size_t bufferSize,
    uint32_t *num_pairs)
{
	_static_assert_1_arg(sizeof(clock_sec_t) % 2 == 0);
	//log_10 2^16 ≈ 4.82
	const size_t max_secs_string_size = 5 * sizeof(clock_sec_t) / 2;
	const size_t max_timestamp_string_size = max_secs_string_size + 1 + 6;

	if (!pBuffer) {
		OSKextPgoMetadataPutMax(position, "INSTANCE", 36);
		OSKextPgoMetadataPutMax(position, "UUID", 36);
		OSKextPgoMetadataPutMax(position, "TIMESTAMP", max_timestamp_string_size);
	} else {
		uuid_string_t instance_uuid_string;
		uuid_unparse(instance_uuid, instance_uuid_string);
		OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
		    "INSTANCE", instance_uuid_string);

		OSSharedPtr<OSData> uuid_data;
		uuid_t uuid;
		uuid_string_t uuid_string;
		uuid_data = kext->copyUUID();
		if (uuid_data) {
			memcpy(uuid, uuid_data->getBytesNoCopy(), sizeof(uuid));
			uuid_unparse(uuid, uuid_string);
			OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
			    "UUID", uuid_string);
		}

		clock_sec_t secs;
		clock_usec_t usecs;
		clock_get_calendar_microtime(&secs, &usecs);
		assert(usecs < 1000000);
		char timestamp[max_timestamp_string_size + 1];
		_static_assert_1_arg(sizeof(long) >= sizeof(clock_sec_t));
		snprintf(timestamp, sizeof(timestamp), "%lu.%06d", (unsigned long)secs, (int)usecs);
		OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
		    "TIMESTAMP", timestamp);
	}

	OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
	    "NAME", kext->getIdentifierCString());

	char versionCString[kOSKextVersionMaxLength];
	OSKextVersionGetString(kext->getVersion(), versionCString, kOSKextVersionMaxLength);
	OSKextPgoMetadataPut(pBuffer, position, bufferSize, num_pairs,
	    "VERSION", versionCString);
}

static
size_t
OSKextPgoMetadataSize(OSKext *kext)
{
	size_t position = 0;
	uuid_t fakeuuid = {};
	OSKextPgoMetadataPutAll(kext, fakeuuid, NULL, &position, 0, NULL);
	return position;
}

int
OSKextGrabPgoDataLocked(OSKext *kext,
    bool metadata,
    uuid_t instance_uuid,
    uint64_t *pSize,
    char *pBuffer,
    uint64_t bufferSize)
{
	int err = 0;

	kernel_section_t *sect_prf_data = NULL;
	kernel_section_t *sect_prf_name = NULL;
	kernel_section_t *sect_prf_cnts = NULL;
	uint64_t size;
	size_t metadata_size = 0;
	size_t offset_to_pairs = 0;

	sect_prf_data = kext->lookupSection("__DATA", "__llvm_prf_data");
	sect_prf_name = kext->lookupSection("__DATA", "__llvm_prf_names");
	if (!sect_prf_name) {
		// kextcache sometimes truncates the section name to 15 chars
		// <rdar://problem/52080551> 16 character section name is truncated to 15 characters by kextcache
		sect_prf_name = kext->lookupSection("__DATA", "__llvm_prf_name");
	}
	sect_prf_cnts = kext->lookupSection("__DATA", "__llvm_prf_cnts");

	// Ignore some sections used by optional PGO variants.
	const char *unused_section = NULL;

	if (!sect_prf_data || !sect_prf_name || !sect_prf_cnts) {
		err = ENOTSUP;
		goto out;
	}

	size = __llvm_profile_get_size_for_buffer_internal(
		(const char*) sect_prf_data->addr, (const char*) sect_prf_data->addr + sect_prf_data->size,
		(const char*) sect_prf_cnts->addr, (const char*) sect_prf_cnts->addr + sect_prf_cnts->size,
		unused_section /* bits */, unused_section /* bits end */,
		(const char*) sect_prf_name->addr, (const char*) sect_prf_name->addr + sect_prf_name->size,
		unused_section /* vtab */, unused_section /* vtab end */,
		unused_section /* vnam */, unused_section /* vnam end */);

	if (metadata) {
		metadata_size = OSKextPgoMetadataSize(kext);
		size += metadata_size;
		size += sizeof(pgo_metadata_footer);
	}


	if (pSize) {
		*pSize = size;
	}

	if (pBuffer && bufferSize) {
		if (bufferSize < size) {
			err = ERANGE;
			goto out;
		}

		err = __llvm_profile_write_buffer_internal(
			pBuffer,
			(const char*) sect_prf_data->addr, (const char*) sect_prf_data->addr + sect_prf_data->size,
			(const char*) sect_prf_cnts->addr, (const char*) sect_prf_cnts->addr + sect_prf_cnts->size,
			unused_section /* bits */, unused_section /* bits end */,
			(const char*) sect_prf_name->addr, (const char*) sect_prf_name->addr + sect_prf_name->size);

		if (err) {
			err = EIO;
			goto out;
		}

		if (metadata) {
			offset_to_pairs = sizeof(struct pgo_metadata_footer) + metadata_size;
			if (offset_to_pairs > UINT32_MAX) {
				err = E2BIG;
				goto out;
			}

			char *end_of_buffer = pBuffer + size;
			struct pgo_metadata_footer *footerp = (struct pgo_metadata_footer *) (end_of_buffer - sizeof(struct pgo_metadata_footer));
			char *metadata_buffer = end_of_buffer - (sizeof(struct pgo_metadata_footer) + metadata_size);

			size_t metadata_position = 0;
			uint32_t num_pairs = 0;
			OSKextPgoMetadataPutAll(kext, instance_uuid, metadata_buffer, &metadata_position, metadata_size, &num_pairs);
			while (metadata_position < metadata_size) {
				metadata_buffer[metadata_position++] = 0;
			}

			struct pgo_metadata_footer footer;
			footer.magic = htonl(0x6d657461);
			footer.number_of_pairs = htonl( num_pairs );
			footer.offset_to_pairs = htonl((uint32_t)offset_to_pairs );
			memcpy(footerp, &footer, sizeof(footer));
		}
	}

out:
	return err;
}


int
OSKextGrabPgoData(uuid_t uuid,
    uint64_t *pSize,
    char *pBuffer,
    uint64_t bufferSize,
    int wait_for_unload,
    int metadata)
{
	int err = 0;
	OSSharedPtr<OSKext> kext;


	IORecursiveLockLock(sKextLock);

	kext = OSKext::lookupKextWithUUID(uuid);
	if (!kext) {
		err = ENOENT;
		goto out;
	}

	if (wait_for_unload) {
		OSKextGrabPgoStruct s;

		s.metadata = metadata;
		s.pSize = pSize;
		s.pBuffer = pBuffer;
		s.bufferSize = bufferSize;
		s.err = EINTR;

		struct list_head *prev = &kext->pendingPgoHead;
		struct list_head *next = kext->pendingPgoHead.next;

		s.list_head.prev = prev;
		s.list_head.next = next;

		prev->next = &s.list_head;
		next->prev = &s.list_head;

		kext.reset();

		IORecursiveLockSleep(sKextLock, &s, THREAD_ABORTSAFE);

		prev = s.list_head.prev;
		next = s.list_head.next;

		prev->next = next;
		next->prev = prev;

		err = s.err;
	} else {
		err = OSKextGrabPgoDataLocked(kext.get(), metadata, kext->instance_uuid, pSize, pBuffer, bufferSize);
	}

out:

	IORecursiveLockUnlock(sKextLock);

	return err;
}

void
OSKextResetPgoCountersLock()
{
	IORecursiveLockLock(sKextLock);
}

void
OSKextResetPgoCountersUnlock()
{
	IORecursiveLockUnlock(sKextLock);
}


extern unsigned int not_in_kdp;

void
OSKextResetPgoCounters()
{
	assert(!not_in_kdp);
	uint32_t count = sLoadedKexts->getCount();
	for (uint32_t i = 0; i < count; i++) {
		OSKext *kext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
		kernel_section_t *sect_prf_cnts = kext->lookupSection("__DATA", "__llvm_prf_cnts");
		if (!sect_prf_cnts) {
			continue;
		}
		memset((void*)sect_prf_cnts->addr, 0, sect_prf_cnts->size);
	}
}

OSSharedPtr<OSDictionary>
OSKext::copyLoadedKextInfoByUUID(
	OSArray * kextIdentifiers,
	OSArray * infoKeys)
{
	OSSharedPtr<OSDictionary> result;
	OSSharedPtr<OSDictionary> kextInfo;
	uint32_t       max_count, i, j;
	uint32_t       idCount = 0;
	uint32_t       idIndex = 0;
	IORecursiveLockLock(sKextLock);
	OSArray *list[2] = {sLoadedKexts.get(), sLoadedDriverKitKexts.get()};
	uint32_t count[2] = {sLoadedKexts->getCount(), sLoadedDriverKitKexts->getCount()};

#if CONFIG_MACF
	/* Is the calling process allowed to query kext info? */
	if (current_task() != kernel_task) {
		int                 macCheckResult      = 0;
		kauth_cred_t        cred                = NULL;

		cred = kauth_cred_get_with_ref();
		macCheckResult = mac_kext_check_query(cred);
		kauth_cred_unref(&cred);

		if (macCheckResult != 0) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Failed to query kext info (MAC policy error 0x%x).",
			    macCheckResult);
			goto finish;
		}
	}
#endif

	/* Empty list of UUIDs is equivalent to no list (get all).
	 */
	if (kextIdentifiers && !kextIdentifiers->getCount()) {
		kextIdentifiers = NULL;
	} else if (kextIdentifiers) {
		idCount = kextIdentifiers->getCount();
	}

	/* Same for keys.
	 */
	if (infoKeys && !infoKeys->getCount()) {
		infoKeys = NULL;
	}

	max_count = count[0] + count[1];
	result = OSDictionary::withCapacity(max_count);
	if (!result) {
		goto finish;
	}

	for (j = 0; j < (sizeof(list) / sizeof(list[0])); j++) {
		for (i = 0; i < count[j]; i++) {
			OSKext       *thisKext     = NULL;        // do not release
			Boolean       includeThis  = true;
			uuid_t        thisKextUUID;
			uuid_t        thisKextTextUUID;
			OSSharedPtr<OSData> uuid_data;
			uuid_string_t uuid_key;

			thisKext = OSDynamicCast(OSKext, list[j]->getObject(i));
			if (!thisKext) {
				continue;
			}

			uuid_data = thisKext->copyUUID();
			if (!uuid_data) {
				continue;
			}

			memcpy(&thisKextUUID, uuid_data->getBytesNoCopy(), sizeof(thisKextUUID));

			uuid_unparse(thisKextUUID, uuid_key);

			uuid_data = thisKext->copyTextUUID();
			if (!uuid_data) {
				continue;
			}
			memcpy(&thisKextTextUUID, uuid_data->getBytesNoCopy(), sizeof(thisKextTextUUID));

			/* Skip current kext if we have a list of UUIDs and
			 * it isn't in the list.
			 */
			if (kextIdentifiers) {
				includeThis = false;

				for (idIndex = 0; idIndex < idCount; idIndex++) {
					const OSString* wantedUUID = OSDynamicCast(OSString,
					    kextIdentifiers->getObject(idIndex));

					uuid_t uuid;
					uuid_parse(wantedUUID->getCStringNoCopy(), uuid);

					if ((0 == uuid_compare(uuid, thisKextUUID))
					    || (0 == uuid_compare(uuid, thisKextTextUUID))) {
						includeThis = true;
						/* Only need to find the first kext if multiple match,
						 * ie. asking for the kernel uuid does not need to find
						 * interface kexts or builtin static kexts.
						 */
						kextIdentifiers->removeObject(idIndex);
						uuid_unparse(uuid, uuid_key);
						break;
					}
				}
			}

			if (!includeThis) {
				continue;
			}

			kextInfo = thisKext->copyInfo(infoKeys);
			if (kextInfo) {
				result->setObject(uuid_key, kextInfo.get());
			}

			if (kextIdentifiers && !kextIdentifiers->getCount()) {
				goto finish;
			}
		}
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSSharedPtr<OSDictionary>
OSKext::copyKextCollectionInfo(
	OSDictionary *requestDict,
	OSArray  *infoKeys)
{
	OSSharedPtr<OSDictionary> result;
	OSString *collectionType = NULL;
	OSObject *rawLoadedState = NULL;
	OSString *loadedState    = NULL;

	kc_kind_t kc_request_kind = KCKindUnknown;
	bool onlyLoaded = false;
	bool onlyUnloaded = false;

#if CONFIG_MACF
	/* Is the calling process allowed to query kext info? */
	if (current_task() != kernel_task) {
		int                 macCheckResult      = 0;
		kauth_cred_t        cred                = NULL;

		cred = kauth_cred_get_with_ref();
		macCheckResult = mac_kext_check_query(cred);
		kauth_cred_unref(&cred);

		if (macCheckResult != 0) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Failed to query kext info (MAC policy error 0x%x).",
			    macCheckResult);
			goto finish;
		}
	}
#endif

	if (infoKeys && !infoKeys->getCount()) {
		infoKeys = NULL;
	}

	collectionType = OSDynamicCast(OSString,
	    _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentCollectionTypeKey));
	if (!collectionType) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Invalid '%s' argument to kext collection info request.",
		    kKextRequestArgumentCollectionTypeKey);
		goto finish;
	}
	if (collectionType->isEqualTo(kKCTypePrimary)) {
		kc_request_kind = KCKindPrimary;
	} else if (collectionType->isEqualTo(kKCTypeSystem)) {
		kc_request_kind = KCKindPageable;
	} else if (collectionType->isEqualTo(kKCTypeAuxiliary)) {
		kc_request_kind = KCKindAuxiliary;
	} else if (collectionType->isEqualTo(kKCTypeCodeless)) {
		kc_request_kind = KCKindNone;
	} else if (!collectionType->isEqualTo(kKCTypeAny)) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogIPCFlag,
		    "Invalid '%s' argument value '%s' to kext collection info request.",
		    kKextRequestArgumentCollectionTypeKey,
		    collectionType->getCStringNoCopy());
		goto finish;
	}

	rawLoadedState = _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentLoadedStateKey);
	if (rawLoadedState) {
		loadedState = OSDynamicCast(OSString, rawLoadedState);
		if (!loadedState) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel |
			    kOSKextLogIPCFlag,
			    "Invalid '%s' argument to kext collection info request.",
			    kKextRequestArgumentLoadedStateKey);
			goto finish;
		}
	}
	if (loadedState) {
		if (loadedState->isEqualTo("Loaded")) {
			onlyLoaded = true;
		} else if (loadedState->isEqualTo("Unloaded")) {
			onlyUnloaded = true;
		} else if (!loadedState->isEqualTo("Any")) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Invalid '%s' argument value '%s' for '%s' collection info",
			    kKextRequestArgumentLoadedStateKey,
			    loadedState->getCStringNoCopy(),
			    collectionType->getCStringNoCopy());
			goto finish;
		}
	}

	result = OSDictionary::withCapacity(sKextsByID->getCount());
	if (!result) {
		goto finish;
	}

	IORecursiveLockLock(sKextLock);
	{         // start block scope
		sKextsByID->iterateObjects(^bool (const OSSymbol *thisKextID, OSObject *obj)
		{
			OSKext       *thisKext    = NULL;  // do not release
			OSSharedPtr<OSDictionary> kextInfo;

			(void)thisKextID;

			thisKext = OSDynamicCast(OSKext, obj);
			if (!thisKext) {
			        return false;
			}

			/*
			 * skip the kext if it came from the wrong collection type
			 * (and the caller requested a specific type)
			 */
			if ((kc_request_kind != KCKindUnknown) && (thisKext->kc_type != kc_request_kind)) {
			        return false;
			}

			/*
			 * respect the caller's desire to find only loaded or
			 * unloaded kexts
			 */
			if (onlyLoaded && (-1U == sLoadedKexts->getNextIndexOfObject(thisKext, 0))) {
			        return false;
			}
			if (onlyUnloaded && (-1U != sLoadedKexts->getNextIndexOfObject(thisKext, 0))) {
			        return false;
			}

			kextInfo = thisKext->copyInfo(infoKeys);
			if (kextInfo) {
			        result->setObject(thisKext->getIdentifier(), kextInfo.get());
			}
			return false;
		});
	} // end block scope
	IORecursiveLockUnlock(sKextLock);

finish:
	return result;
}

/* static */
OSSharedPtr<OSArray>
OSKext::copyDextsInfo(
	OSArray *kextIdentifiers,
	OSArray *infoKeys)
{
	OSSharedPtr<OSArray> result = NULL;
	uint32_t idCount = 0;
	bool getActive = false;
	bool getLoaded = false;
	bool getUnloaded = false;
	bool getPendingUpgrade = false;
	unsigned int avgDextCount = 0;

#if CONFIG_MACF
	/* Is the calling process allowed to query dext info? */
	if (current_task() != kernel_task) {
		int                 macCheckResult      = 0;
		kauth_cred_t        cred                = NULL;

		cred = kauth_cred_get_with_ref();
		macCheckResult = mac_kext_check_query(cred);
		kauth_cred_unref(&cred);

		if (macCheckResult != 0) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Failed to query kext info (MAC policy error 0x%x).",
			    macCheckResult);
			goto finish;
		}
	}
#endif
	/*
	 * No infoKeys means return everything we
	 * know about the dexts.
	 */
	if (infoKeys && !infoKeys->getCount()) {
		infoKeys = NULL;
	}

	/*
	 * Empty list of bundle ids is equivalent to
	 * no list (get all).
	 */
	if (kextIdentifiers && !kextIdentifiers->getCount()) {
		kextIdentifiers = NULL;
	} else if (kextIdentifiers) {
		idCount = kextIdentifiers->getCount();
	}

	/*
	 * Caller can specify which state of dexts to query.
	 */
	if (infoKeys && _OSArrayContainsCString(infoKeys, kOSBundleDextStateActiveKey)) {
		getActive = true;
	}
	if (infoKeys && _OSArrayContainsCString(infoKeys, kOSBundleDextStateActiveLoadedKey)) {
		getLoaded = true;
	}
	if (infoKeys && _OSArrayContainsCString(infoKeys, kOSBundleDextStateActiveUnloadedKey)) {
		getUnloaded = true;
	}
	if (infoKeys && _OSArrayContainsCString(infoKeys, kOSBundleDextStatePendingUpgradeKey)) {
		getPendingUpgrade = true;
	}

	/*
	 * By default we are going to return all active and pendingUpgrade dexts
	 * only.
	 */
	if (!(getActive || getLoaded || getUnloaded || getPendingUpgrade)) {
		getActive = true;
		getPendingUpgrade = true;
	}

	/*
	 * We return a dictionary of dexts
	 * for every group requested.
	 */
	avgDextCount = sLoadedDriverKitKexts->getCount() + sDriverKitToUpgradeByID->getCount();
	result = OSArray::withCapacity(avgDextCount);
	if (!result) {
		goto finish;
	}

	IORecursiveLockLock(sKextLock);
	{ // start block scope
		if (getActive || getLoaded || getUnloaded) {
			sKextsByID->iterateObjects(^bool (const OSSymbol *thisKextID, OSObject *obj)
			{
				OSKext *thisKext = NULL;  // do not release
				OSSharedPtr<OSDictionary> kextInfo;
				Boolean includeThis = true;
				(void)thisKextID;

				thisKext = OSDynamicCast(OSKext, obj);
				if (!thisKext || !thisKext->isDriverKit()) {
				        return false;
				}

				/*
				 * Skip current dext if we have a list of bundle IDs and
				 * it isn't in the list.
				 */
				if (kextIdentifiers) {
				        includeThis = false;

				        for (uint32_t idIndex = 0; idIndex < idCount; idIndex++) {
				                const OSString * thisRequestID = OSDynamicCast(OSString,
				                kextIdentifiers->getObject(idIndex));
				                if (thisKextID->isEqualTo(thisRequestID)) {
				                        includeThis = true;
				                        break;
						}
					}
				}

				if (!includeThis) {
				        return false;
				}

				OSSharedPtr<OSString> state;
				if (sLoadedDriverKitKexts->getNextIndexOfObject(thisKext, 0) == -1U) {
				        if (!(getActive || getUnloaded)) {
				                return false;
					}
				        state = OSString::withCString(kOSBundleDextStateActiveUnloadedKey);
				} else {
				        if (!(getActive || getLoaded)) {
				                return false;
					}
				        state = OSString::withCString(kOSBundleDextStateActiveLoadedKey);
				}

				kextInfo = thisKext->copyInfo(infoKeys);
				if (kextInfo) {
				        kextInfo->setObject(kOSBundleDextStateKey, state.get());
				        result->setObject(kextInfo.get());
				}

				return false;
			});
		}

		if (getPendingUpgrade) {
			sDriverKitToUpgradeByID->iterateObjects(^bool (const OSSymbol *thisKextID, OSObject *obj)
			{
				OSKext *thisKext = NULL;  // do not release
				OSSharedPtr<OSDictionary> kextInfo;
				Boolean includeThis = true;
				(void)thisKextID;

				thisKext = OSDynamicCast(OSKext, obj);
				if (!thisKext) {
				        return false;
				}
				__assert_only bool isDext = thisKext->isDriverKit();
				assert(isDext == true);

				/*
				 * Skip current dext if we have a list of bundle IDs and
				 * it isn't in the list.
				 */
				if (kextIdentifiers) {
				        includeThis = false;

				        for (uint32_t idIndex = 0; idIndex < idCount; idIndex++) {
				                const OSString * thisRequestID = OSDynamicCast(OSString,
				                kextIdentifiers->getObject(idIndex));
				                if (thisKextID->isEqualTo(thisRequestID)) {
				                        includeThis = true;
				                        break;
						}
					}
				}

				if (!includeThis) {
				        return false;
				}

				kextInfo = thisKext->copyInfo(infoKeys);
				if (kextInfo) {
				        OSSharedPtr<OSString> state = OSString::withCString(kOSBundleDextStatePendingUpgradeKey);
				        kextInfo->setObject(kOSBundleDextStateKey, state.get());
				        result->setObject(kextInfo.get());
				}
				return false;
			});
		}
	} // end block scope
	IORecursiveLockUnlock(sKextLock);
finish:
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
OSSharedPtr<OSDictionary>
OSKext::copyLoadedKextInfo(
	OSArray * kextIdentifiers,
	OSArray * infoKeys)
{
	OSSharedPtr<OSDictionary> result;
	uint32_t       idCount = 0;
	bool           onlyLoaded;

	IORecursiveLockLock(sKextLock);

#if CONFIG_MACF
	/* Is the calling process allowed to query kext info? */
	if (current_task() != kernel_task) {
		int                 macCheckResult      = 0;
		kauth_cred_t        cred                = NULL;

		cred = kauth_cred_get_with_ref();
		macCheckResult = mac_kext_check_query(cred);
		kauth_cred_unref(&cred);

		if (macCheckResult != 0) {
			OSKextLog(/* kext */ NULL,
			    kOSKextLogErrorLevel | kOSKextLogLoadFlag,
			    "Failed to query kext info (MAC policy error 0x%x).",
			    macCheckResult);
			goto finish;
		}
	}
#endif

	/* Empty list of bundle ids is equivalent to no list (get all).
	 */
	if (kextIdentifiers && !kextIdentifiers->getCount()) {
		kextIdentifiers = NULL;
	} else if (kextIdentifiers) {
		idCount = kextIdentifiers->getCount();
	}

	/* Same for keys.
	 */
	if (infoKeys && !infoKeys->getCount()) {
		infoKeys = NULL;
	}

	onlyLoaded =  (!infoKeys || !_OSArrayContainsCString(infoKeys, kOSBundleAllPrelinkedKey));

	result = OSDictionary::withCapacity(128);
	if (!result) {
		goto finish;
	}

#if 0
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_kernel_slide 0x%lx \n",
	    vm_kernel_slide);
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_kernel_stext 0x%lx vm_kernel_etext 0x%lx \n",
	    vm_kernel_stext, vm_kernel_etext);
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_kernel_base 0x%lx vm_kernel_top 0x%lx \n",
	    vm_kernel_base, vm_kernel_top);
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_kext_base 0x%lx vm_kext_top 0x%lx \n",
	    vm_kext_base, vm_kext_top);
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_prelink_stext 0x%lx vm_prelink_etext 0x%lx \n",
	    vm_prelink_stext, vm_prelink_etext);
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_prelink_sinfo 0x%lx vm_prelink_einfo 0x%lx \n",
	    vm_prelink_sinfo, vm_prelink_einfo);
	OSKextLog(/* kext */ NULL,
	    kOSKextLogErrorLevel |
	    kOSKextLogGeneralFlag,
	    "kaslr: vm_slinkedit 0x%lx vm_elinkedit 0x%lx \n",
	    vm_slinkedit, vm_elinkedit);
#endif
	{         // start block scope
		sKextsByID->iterateObjects(^bool (const OSSymbol * thisKextID, OSObject * obj)
		{
			OSKext       * thisKext     = NULL;        // do not release
			Boolean        includeThis  = true;
			OSSharedPtr<OSDictionary> kextInfo;

			thisKext = OSDynamicCast(OSKext, obj);
			if (!thisKext) {
			        return false;
			}

			/* Skip current kext if not yet started and caller didn't request all.
			 */
			if (onlyLoaded && (-1U == sLoadedKexts->getNextIndexOfObject(thisKext, 0))) {
			        return false;
			}

			/* Skip current kext if we have a list of bundle IDs and
			 * it isn't in the list.
			 */
			if (kextIdentifiers) {
			        includeThis = false;

			        for (uint32_t idIndex = 0; idIndex < idCount; idIndex++) {
			                const OSString * thisRequestID = OSDynamicCast(OSString,
			                kextIdentifiers->getObject(idIndex));
			                if (thisKextID->isEqualTo(thisRequestID)) {
			                        includeThis = true;
			                        break;
					}
				}
			}

			if (!includeThis) {
			        return false;
			}

			kextInfo = thisKext->copyInfo(infoKeys);
			if (kextInfo) {
			        result->setObject(thisKext->getIdentifier(), kextInfo.get());
			}
			return false;
		});
	}         // end block scope

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
* Any info that needs to do allocations must goto finish on alloc
* failure. Info that is just a lookup should just not set the object
* if the info does not exist.
*********************************************************************/
#define _OSKextLoadInfoDictCapacity   (12)

OSSharedPtr<OSDictionary>
OSKext::copyInfo(OSArray * infoKeys)
{
	OSSharedPtr<OSDictionary>  result;
	bool                       success                     = false;
	OSSharedPtr<OSData>        headerData;
	OSSharedPtr<OSData>        logData;
	OSSharedPtr<OSNumber>      cpuTypeNumber;
	OSSharedPtr<OSNumber>      cpuSubtypeNumber;
	OSString                 * versionString               = NULL;        // do not release
	OSString                 * bundleType                  = NULL;        // do not release
	uint32_t                   executablePathCStringSize   = 0;
	char                     * executablePathCString       = NULL;        // must kfree
	OSSharedPtr<OSString>      executablePathString;
	OSSharedPtr<OSData>        uuid;
	OSSharedPtr<OSArray>       dependencyLoadTags;
	OSSharedPtr<OSCollectionIterator>      metaClassIterator;
	OSSharedPtr<OSArray>       metaClassInfo;
	OSSharedPtr<OSDictionary>  metaClassDict;
	OSMetaClass              * thisMetaClass               = NULL;        // do not release
	OSSharedPtr<OSString>      metaClassName;
	OSSharedPtr<OSString>      superclassName;
	kc_format_t                kcformat;
	uint32_t                   count, i;

	result = OSDictionary::withCapacity(_OSKextLoadInfoDictCapacity);
	if (!result) {
		goto finish;
	}


	/* Empty keys means no keys, but NULL is quicker to check.
	 */
	if (infoKeys && !infoKeys->getCount()) {
		infoKeys = NULL;
	}

	if (!PE_get_primary_kc_format(&kcformat)) {
		goto finish;
	}

	/* Headers, CPU type, and CPU subtype.
	 */
	if (!infoKeys ||
	    _OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleLogStringsKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey)) {
		if (linkedExecutable && !isInterface()) {
			kernel_mach_header_t *kext_mach_hdr = (kernel_mach_header_t *)
			    linkedExecutable->getBytesNoCopy();

#if !SECURE_KERNEL || XNU_TARGET_OS_OSX
			// do not return macho header info on shipping embedded - 19095897
			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleMachOHeadersKey)) {
				kernel_mach_header_t *  temp_kext_mach_hdr;
				struct load_command *   lcp;

				headerData = OSData::withBytes(kext_mach_hdr,
				    (u_int) (sizeof(*kext_mach_hdr) + kext_mach_hdr->sizeofcmds));
				if (!headerData) {
					goto finish;
				}

				// unslide any vmaddrs we return to userspace - 10726716
				temp_kext_mach_hdr = (kernel_mach_header_t *)
				    headerData->getBytesNoCopy();
				if (temp_kext_mach_hdr == NULL) {
					goto finish;
				}

				lcp = (struct load_command *) (temp_kext_mach_hdr + 1);
				for (i = 0; (i < temp_kext_mach_hdr->ncmds) && !flags.unslidMachO; i++) {
					if (lcp->cmd == LC_SEGMENT_KERNEL) {
						kernel_segment_command_t *  segp;
						kernel_section_t *          secp;

						segp = (kernel_segment_command_t *) lcp;
						// 10543468 - if we jettisoned __LINKEDIT clear size info
						if (flags.jettisonLinkeditSeg) {
							if (strncmp(segp->segname, SEG_LINKEDIT, sizeof(segp->segname)) == 0) {
								segp->vmsize = 0;
								segp->fileoff = 0;
								segp->filesize = 0;
							}
						}

#if __arm__ || __arm64__
						// iBoot disregards zero-size segments, just set their addresses to gVirtBase
						// and unslide them to avoid vm assertion failures / kernel logging breakage.
						if (segp->vmsize == 0 && segp->vmaddr < gVirtBase) {
							segp->vmaddr = gVirtBase;
							for (secp = firstsect(segp); secp != NULL; secp = nextsect(segp, secp)) {
								secp->size = 0; // paranoia :)
								secp->addr = gVirtBase;
							}
						}
#endif

#if 0
						OSKextLog(/* kext */ NULL,
						    kOSKextLogErrorLevel |
						    kOSKextLogGeneralFlag,
						    "%s: LC_SEGMENT_KERNEL segname '%s' vmaddr 0x%llX 0x%lX vmsize %llu nsects %u",
						    __FUNCTION__, segp->segname, segp->vmaddr,
						    VM_KERNEL_UNSLIDE(segp->vmaddr),
						    segp->vmsize, segp->nsects);
						if ((VM_KERNEL_IS_SLID(segp->vmaddr) == false) &&
						    (VM_KERNEL_IS_KEXT(segp->vmaddr) == false) &&
						    (VM_KERNEL_IS_PRELINKTEXT(segp->vmaddr) == false) &&
						    (VM_KERNEL_IS_PRELINKINFO(segp->vmaddr) == false) &&
						    (VM_KERNEL_IS_KEXT_LINKEDIT(segp->vmaddr) == false)) {
							OSKextLog(/* kext */ NULL,
							    kOSKextLogErrorLevel |
							    kOSKextLogGeneralFlag,
							    "%s: not in kext range - vmaddr 0x%llX vm_kext_base 0x%lX vm_kext_top 0x%lX",
							    __FUNCTION__, segp->vmaddr, vm_kext_base, vm_kext_top);
						}
#endif
						segp->vmaddr = ml_static_unslide(segp->vmaddr);

						for (secp = firstsect(segp); secp != NULL; secp = nextsect(segp, secp)) {
							secp->addr = ml_static_unslide(secp->addr);
						}
					}
					lcp = (struct load_command *)((caddr_t)lcp + lcp->cmdsize);
				}
				result->setObject(kOSBundleMachOHeadersKey, headerData.get());
			}
#endif // !SECURE_KERNEL || XNU_TARGET_OS_OSX

			if (_OSArrayContainsCString(infoKeys, kOSBundleLogStringsKey)) {
				osLogDataHeaderRef *header;
				char headerBytes[offsetof(osLogDataHeaderRef, sections) + NUM_OS_LOG_SECTIONS * sizeof(header->sections[0])];

				void *os_log_data               = NULL;
				void *cstring_data              = NULL;
				void *asan_cstring_data         = NULL;
				unsigned long os_log_size       = 0;
				unsigned long cstring_size      = 0;
				unsigned long asan_cstring_size = 0;
				uint32_t os_log_offset          = 0;
				uint32_t cstring_offset         = 0;
				uint32_t asan_cstring_offset    = 0;
				bool res;

				os_log_data         = getsectdatafromheader(kext_mach_hdr, "__TEXT", "__os_log", &os_log_size);
				cstring_data        = getsectdatafromheader(kext_mach_hdr, "__TEXT", "__cstring", &cstring_size);
				asan_cstring_data   = getsectdatafromheader(kext_mach_hdr, "__TEXT", "__asan_cstring", &asan_cstring_size);

				/*
				 * If the addresses in the Mach-O header are unslid, manually
				 * slide them to allow for dereferencing.
				 */
				if (flags.unslidMachO) {
					os_log_data = (os_log_data != nullptr) ? (void*)ml_static_slide((vm_offset_t)os_log_data) : nullptr;
					cstring_data = (cstring_data != nullptr) ? (void*)ml_static_slide((vm_offset_t)cstring_data) : nullptr;
					asan_cstring_data = (asan_cstring_data != nullptr) ? (void*)ml_static_slide((vm_offset_t)asan_cstring_data) : nullptr;
				}

				os_log_offset       = (uintptr_t)os_log_data - (uintptr_t)kext_mach_hdr;
				cstring_offset      = (uintptr_t)cstring_data - (uintptr_t)kext_mach_hdr;
				asan_cstring_offset = (uintptr_t)asan_cstring_data - (uintptr_t)kext_mach_hdr;

				header             = (osLogDataHeaderRef *) headerBytes;
				header->version    = OS_LOG_HDR_VERSION;
				header->sect_count = NUM_OS_LOG_SECTIONS;
				header->sections[OS_LOG_SECT_IDX].sect_offset       = os_log_offset;
				header->sections[OS_LOG_SECT_IDX].sect_size         = (uint32_t) os_log_size;
				header->sections[CSTRING_SECT_IDX].sect_offset      = cstring_offset;
				header->sections[CSTRING_SECT_IDX].sect_size        = (uint32_t) cstring_size;
				header->sections[ASAN_CSTRING_SECT_IDX].sect_offset = asan_cstring_offset;
				header->sections[ASAN_CSTRING_SECT_IDX].sect_size   = (uint32_t) asan_cstring_size;


				logData = OSData::withValue(*header);
				if (!logData) {
					goto finish;
				}
				res = logData->appendBytes(&(header->sections[0]), (u_int)(header->sect_count * sizeof(header->sections[0])));
				if (!res) {
					goto finish;
				}
				if (os_log_data) {
					res = logData->appendBytes(os_log_data, (u_int)header->sections[OS_LOG_SECT_IDX].sect_size);
					if (!res) {
						goto finish;
					}
				}
				if (cstring_data) {
					res = logData->appendBytes(cstring_data, (u_int)header->sections[CSTRING_SECT_IDX].sect_size);
					if (!res) {
						goto finish;
					}
				}
				if (asan_cstring_data) {
					res = logData->appendBytes(asan_cstring_data, (u_int)header->sections[ASAN_CSTRING_SECT_IDX].sect_size);
					if (!res) {
						goto finish;
					}
				}
				result->setObject(kOSBundleLogStringsKey, logData.get());
			}

			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCPUTypeKey)) {
				cpuTypeNumber = OSNumber::withNumber(
					(uint64_t) kext_mach_hdr->cputype,
					8 * sizeof(kext_mach_hdr->cputype));
				if (!cpuTypeNumber) {
					goto finish;
				}
				result->setObject(kOSBundleCPUTypeKey, cpuTypeNumber.get());
			}

			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCPUSubtypeKey)) {
				cpuSubtypeNumber = OSNumber::withNumber(
					(uint64_t) kext_mach_hdr->cpusubtype,
					8 * sizeof(kext_mach_hdr->cpusubtype));
				if (!cpuSubtypeNumber) {
					goto finish;
				}
				result->setObject(kOSBundleCPUSubtypeKey, cpuSubtypeNumber.get());
			}
		} else {
			if (isDriverKit() && _OSArrayContainsCString(infoKeys, kOSBundleLogStringsKey)) {
				osLogDataHeaderRef *header;
				char headerBytes[offsetof(osLogDataHeaderRef, sections) + NUM_OS_LOG_SECTIONS * sizeof(header->sections[0])];
				bool res;

				header             = (osLogDataHeaderRef *) headerBytes;
				header->version    = OS_LOG_HDR_VERSION;
				header->sect_count = NUM_OS_LOG_SECTIONS;
				header->sections[OS_LOG_SECT_IDX].sect_offset       = 0;
				header->sections[OS_LOG_SECT_IDX].sect_size         = (uint32_t) 0;
				header->sections[CSTRING_SECT_IDX].sect_offset      = 0;
				header->sections[CSTRING_SECT_IDX].sect_size        = (uint32_t) 0;
				header->sections[ASAN_CSTRING_SECT_IDX].sect_offset = 0;
				header->sections[ASAN_CSTRING_SECT_IDX].sect_size   = (uint32_t) 0;

				logData = OSData::withValue(*header);
				if (!logData) {
					goto finish;
				}
				res = logData->appendBytes(&(header->sections[0]), (u_int)(header->sect_count * sizeof(header->sections[0])));
				if (!res) {
					goto finish;
				}
				result->setObject(kOSBundleLogStringsKey, logData.get());
			}
		}
	}

	/* CFBundleIdentifier. We set this regardless because it's just stupid not to.
	 */
	result->setObject(kCFBundleIdentifierKey, bundleID.get());

	/* kOSBundleDextUniqueIdentifierKey if present.
	 */
	if (isDriverKit() && dextUniqueID != NULL) {
		result->setObject(kOSBundleDextUniqueIdentifierKey, dextUniqueID.get());
	}

	/* CFBundlePackageType
	 */
	bundleType = infoDict ? OSDynamicCast(OSString, infoDict->getObject(kCFBundlePackageTypeKey)): NULL;
	if (bundleType) {
		result->setObject(kCFBundlePackageTypeKey, bundleType);
	}

	/* CFBundleVersion.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kCFBundleVersionKey)) {
		versionString = OSDynamicCast(OSString,
		    getPropertyForHostArch(kCFBundleVersionKey));
		if (versionString) {
			result->setObject(kCFBundleVersionKey, versionString);
		}
	}

	/* OSBundleCompatibleVersion.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCompatibleVersionKey)) {
		versionString = OSDynamicCast(OSString,
		    getPropertyForHostArch(kOSBundleCompatibleVersionKey));
		if (versionString) {
			result->setObject(kOSBundleCompatibleVersionKey, versionString);
		}
	}

	/* Path.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundlePathKey)) {
		if (path) {
			result->setObject(kOSBundlePathKey, path.get());
		}
	}


	/* OSBundleExecutablePath.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecutablePathKey)) {
		if (path && executableRelPath) {
			uint32_t pathLength = path->getLength();         // gets incremented below

			// +1 for slash, +1 for \0
			executablePathCStringSize = pathLength + executableRelPath->getLength() + 2;

			executablePathCString = (char *)kalloc_data_tag(executablePathCStringSize,
			    Z_WAITOK, VM_KERN_MEMORY_OSKEXT);         // +1 for \0
			if (!executablePathCString) {
				goto finish;
			}
			strlcpy(executablePathCString, path->getCStringNoCopy(),
			    executablePathCStringSize);
			executablePathCString[pathLength++] = '/';
			executablePathCString[pathLength++] = '\0';
			strlcat(executablePathCString, executableRelPath->getCStringNoCopy(),
			    executablePathCStringSize);

			executablePathString = OSString::withCString(executablePathCString);

			if (!executablePathString) {
				goto finish;
			}

			result->setObject(kOSBundleExecutablePathKey, executablePathString.get());
		} else if (flags.builtin) {
			result->setObject(kOSBundleExecutablePathKey, bundleID.get());
		} else if (isDriverKit()) {
			if (path) {
				// +1 for slash, +1 for \0
				uint32_t pathLength = path->getLength();
				executablePathCStringSize = pathLength + 2;

				executablePathCString = (char *)kalloc_data_tag(executablePathCStringSize,
				    Z_WAITOK, VM_KERN_MEMORY_OSKEXT);
				if (!executablePathCString) {
					goto finish;
				}
				strlcpy(executablePathCString, path->getCStringNoCopy(), executablePathCStringSize);
				executablePathCString[pathLength++] = '/';
				executablePathCString[pathLength++] = '\0';

				executablePathString = OSString::withCString(executablePathCString);

				if (!executablePathString) {
					goto finish;
				}

				result->setObject(kOSBundleExecutablePathKey, executablePathString.get());
			}
		}
	}

	/* UUID, if the kext has one.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleUUIDKey)) {
		uuid = copyUUID();
		if (uuid) {
			result->setObject(kOSBundleUUIDKey, uuid.get());
		}
	}
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleTextUUIDKey)) {
		uuid = copyTextUUID();
		if (uuid) {
			result->setObject(kOSBundleTextUUIDKey, uuid.get());
		}
	}

	/*
	 * Info.plist digest
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSKextInfoPlistDigestKey)) {
		OSData *digest;
		digest = infoDict ? OSDynamicCast(OSData, infoDict->getObject(kOSKextInfoPlistDigestKey)) : NULL;
		if (digest) {
			result->setObject(kOSKextInfoPlistDigestKey, digest);
		}
	}

	/*
	 * Collection type
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSKextBundleCollectionTypeKey)) {
		result->setObject(kOSKextBundleCollectionTypeKey, OSString::withCString(getKCTypeString()));
	}

	/*
	 * Collection availability
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSKextAuxKCAvailabilityKey)) {
		result->setObject(kOSKextAuxKCAvailabilityKey,
		    isLoadable() ? kOSBooleanTrue : kOSBooleanFalse);
	}

	/*
	 * Allows user load
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleAllowUserLoadKey)) {
		OSBoolean *allowUserLoad = OSDynamicCast(OSBoolean, getPropertyForHostArch(kOSBundleAllowUserLoadKey));
		if (allowUserLoad) {
			result->setObject(kOSBundleAllowUserLoadKey, allowUserLoad);
		}
	}

	/*
	 * Bundle Dependencies (OSBundleLibraries)
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLibrariesKey)) {
		OSDictionary *libraries = OSDynamicCast(OSDictionary, getPropertyForHostArch(kOSBundleLibrariesKey));
		if (libraries) {
			result->setObject(kOSBundleLibrariesKey, libraries);
		}
	}

	/*****
	 * OSKernelResource, OSBundleIsInterface, OSBundlePrelinked, OSBundleStarted.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSKernelResourceKey)) {
		result->setObject(kOSKernelResourceKey,
		    isKernelComponent() ? kOSBooleanTrue : kOSBooleanFalse);
	}

	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleIsInterfaceKey)) {
		result->setObject(kOSBundleIsInterfaceKey,
		    isInterface() ? kOSBooleanTrue : kOSBooleanFalse);
	}

	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundlePrelinkedKey)) {
		result->setObject(kOSBundlePrelinkedKey,
		    isPrelinked() ? kOSBooleanTrue : kOSBooleanFalse);
	}

	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleStartedKey)) {
		result->setObject(kOSBundleStartedKey,
		    isStarted() ? kOSBooleanTrue : kOSBooleanFalse);
	}

	/* LoadTag (Index).
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadTagKey)) {
		OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber((unsigned long long)loadTag,
		    /* numBits */ 8 * sizeof(loadTag));
		if (!scratchNumber) {
			goto finish;
		}
		result->setObject(kOSBundleLoadTagKey, scratchNumber.get());
	}

	/* LoadAddress, LoadSize.
	 */
	if (!infoKeys ||
	    _OSArrayContainsCString(infoKeys, kOSBundleLoadAddressKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleLoadSizeKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleExecLoadAddressKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleExecLoadSizeKey) ||
	    _OSArrayContainsCString(infoKeys, kOSBundleWiredSizeKey)) {
		bool is_dext = isDriverKit();
		if (isInterface() || flags.builtin || linkedExecutable || is_dext) {
			/* These go to userspace via serialization, so we don't want any doubts
			 * about their size.
			 */
			uint64_t    loadAddress     = 0;
			uint32_t    loadSize        = 0;
			uint32_t    wiredSize       = 0;
			uint64_t    execLoadAddress = 0;
			uint32_t    execLoadSize    = 0;

			/* Interfaces always report 0 load address & size.
			 * Just the way they roll.
			 *
			 * xxx - leaving in # when we have a linkedExecutable...a kernelcomp
			 * xxx - shouldn't have one!
			 */

			if (flags.builtin || linkedExecutable) {
				kernel_mach_header_t     *mh  = NULL;
				kernel_segment_command_t *seg = NULL;

				if (flags.builtin) {
					loadAddress = kmod_info->address;
					loadSize    = (uint32_t)kmod_info->size;
				} else {
					loadAddress = (uint64_t)linkedExecutable->getBytesNoCopy();
					loadSize = linkedExecutable->getLength();
				}
				mh = (kernel_mach_header_t *)loadAddress;
				loadAddress = ml_static_unslide(loadAddress);

				/* Walk through the kext, looking for the first executable
				 * segment in case we were asked for its size/address.
				 */
				for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
					if (seg->initprot & VM_PROT_EXECUTE) {
						execLoadAddress = (flags.unslidMachO) ? seg->vmaddr : ml_static_unslide(seg->vmaddr);
						execLoadSize = (uint32_t)seg->vmsize;
						break;
					}
				}

				/* If we have a kmod_info struct, calculated the wired size
				 * from that. Otherwise it's the full load size.
				 */
				if (kmod_info) {
					wiredSize = loadSize - (uint32_t)kmod_info->hdr_size;
				} else {
					wiredSize = loadSize;
				}
			} else if (is_dext) {
				/*
				 * DriverKit userspace executables do not have a kernel linkedExecutable,
				 * so we "fake" their address range with the LoadTag.
				 */
				if (loadTag) {
					loadAddress = execLoadAddress = loadTag;
					loadSize = execLoadSize = 1;
				}
			}

			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadAddressKey)) {
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
					(unsigned long long)(loadAddress),
					/* numBits */ 8 * sizeof(loadAddress));
				if (!scratchNumber) {
					goto finish;
				}
				result->setObject(kOSBundleLoadAddressKey, scratchNumber.get());
			}
			if (kcformat == KCFormatStatic || kcformat == KCFormatKCGEN) {
				if ((!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleCacheLoadAddressKey))
				    && loadAddress && loadSize) {
					void *baseAddress = PE_get_kc_baseaddress(KCKindPrimary);
					if (!baseAddress) {
						goto finish;
					}

					OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
						(unsigned long long)ml_static_unslide((vm_offset_t)baseAddress),
						/* numBits */ 8 * sizeof(loadAddress));
					if (!scratchNumber) {
						goto finish;
					}
					result->setObject(kOSBundleCacheLoadAddressKey, scratchNumber.get());
				}
				if ((!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleKextsInKernelTextKey))
				    && (this == sKernelKext) && gBuiltinKmodsCount) {
					result->setObject(kOSBundleKextsInKernelTextKey, kOSBooleanTrue);
				}
			}

			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecLoadAddressKey)) {
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
					(unsigned long long)(execLoadAddress),
					/* numBits */ 8 * sizeof(execLoadAddress));
				if (!scratchNumber) {
					goto finish;
				}
				result->setObject(kOSBundleExecLoadAddressKey, scratchNumber.get());
			}
			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleLoadSizeKey)) {
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
					(unsigned long long)(loadSize),
					/* numBits */ 8 * sizeof(loadSize));
				if (!scratchNumber) {
					goto finish;
				}
				result->setObject(kOSBundleLoadSizeKey, scratchNumber.get());
			}
			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleExecLoadSizeKey)) {
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
					(unsigned long long)(execLoadSize),
					/* numBits */ 8 * sizeof(execLoadSize));
				if (!scratchNumber) {
					goto finish;
				}
				result->setObject(kOSBundleExecLoadSizeKey, scratchNumber.get());
			}
			if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleWiredSizeKey)) {
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
					(unsigned long long)(wiredSize),
					/* numBits */ 8 * sizeof(wiredSize));
				if (!scratchNumber) {
					goto finish;
				}
				result->setObject(kOSBundleWiredSizeKey, scratchNumber.get());
			}
		}
	}

	/* OSBundleDependencies. In descending order for
	 * easy compatibility with kextstat(8).
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleDependenciesKey)) {
		if ((count = getNumDependencies())) {
			dependencyLoadTags = OSArray::withCapacity(count);
			result->setObject(kOSBundleDependenciesKey, dependencyLoadTags.get());

			i = count - 1;
			do {
				OSKext * dependency = OSDynamicCast(OSKext,
				    dependencies->getObject(i));

				if (!dependency) {
					continue;
				}
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
					(unsigned long long)dependency->getLoadTag(),
					/* numBits*/ 8 * sizeof(loadTag));
				if (!scratchNumber) {
					goto finish;
				}
				dependencyLoadTags->setObject(scratchNumber.get());
			} while (i--);
		}
	}

	/* OSBundleMetaClasses.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleClassesKey)) {
		if (metaClasses && metaClasses->getCount()) {
			metaClassIterator = OSCollectionIterator::withCollection(metaClasses.get());
			metaClassInfo = OSArray::withCapacity(metaClasses->getCount());
			if (!metaClassIterator || !metaClassInfo) {
				goto finish;
			}
			result->setObject(kOSBundleClassesKey, metaClassInfo.get());

			while ((thisMetaClass = OSDynamicCast(OSMetaClass,
			    metaClassIterator->getNextObject()))) {
				metaClassDict = OSDictionary::withCapacity(3);
				if (!metaClassDict) {
					goto finish;
				}

				metaClassName = OSString::withCString(thisMetaClass->getClassName());
				if (thisMetaClass->getSuperClass()) {
					superclassName = OSString::withCString(
						thisMetaClass->getSuperClass()->getClassName());
				}
				OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(thisMetaClass->getInstanceCount(),
				    8 * sizeof(unsigned int));

				/* Bail if any of the essentials is missing. The root class lacks a superclass,
				 * of course.
				 */
				if (!metaClassDict || !metaClassName || !scratchNumber) {
					goto finish;
				}

				metaClassInfo->setObject(metaClassDict.get());
				metaClassDict->setObject(kOSMetaClassNameKey, metaClassName.get());
				if (superclassName) {
					metaClassDict->setObject(kOSMetaClassSuperclassNameKey, superclassName.get());
				}
				metaClassDict->setObject(kOSMetaClassTrackingCountKey, scratchNumber.get());
			}
		}
	}

	/* OSBundleRetainCount.
	 */
	if (!infoKeys || _OSArrayContainsCString(infoKeys, kOSBundleRetainCountKey)) {
		{
			int kextRetainCount = getRetainCount() - 1;
			if (isLoaded()) {
				kextRetainCount--;
			}
			OSSharedPtr<OSNumber> scratchNumber = OSNumber::withNumber(
				(int)kextRetainCount,
				/* numBits*/ 8 * sizeof(int));
			if (scratchNumber) {
				result->setObject(kOSBundleRetainCountKey, scratchNumber.get());
			}
		}
	}

	success = true;

finish:
	if (executablePathCString) {
		kfree_data(executablePathCString, executablePathCStringSize);
	}
	if (!success) {
		result.reset();
	}
	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
bool
OSKext::copyUserExecutablePath(const OSSymbol * bundleID, char * pathResult, size_t pathSize)
{
	bool ok;
	OSSharedPtr<OSKext> kext;

	IORecursiveLockLock(sKextLock);
	kext.reset(OSDynamicCast(OSKext, sKextsByID->getObject(bundleID)), OSRetain);
	IORecursiveLockUnlock(sKextLock);

	if (!kext || !kext->path || !kext->userExecutableRelPath) {
		return false;
	}
	snprintf(pathResult, pathSize, "%s/Contents/MacOS/%s",
	    kext->path->getCStringNoCopy(),
	    kext->userExecutableRelPath->getCStringNoCopy());
	ok = true;

	return ok;
}

/*********************************************************************
*********************************************************************/
/* static */
OSReturn
OSKext::requestResource(
	const char                    * kextIdentifierCString,
	const char                    * resourceNameCString,
	OSKextRequestResourceCallback   callback,
	void                          * context,
	OSKextRequestTag              * requestTagOut)
{
	OSReturn                        result = kOSReturnError;
	OSSharedPtr<OSKext>             callbackKext;        // looked up

	OSKextRequestTag   requestTag      = -1;
	OSSharedPtr<OSNumber>           requestTagNum;
	OSSharedPtr<OSDictionary>       requestDict;
	OSSharedPtr<OSString>           kextIdentifier;
	OSSharedPtr<OSString>           resourceName;

	OSSharedPtr<OSDictionary>       callbackRecord;
	OSSharedPtr<OSValueObject<OSKextRequestResourceCallback> > callbackWrapper;

	OSSharedPtr<OSValueObject<void *> > contextWrapper;

	IORecursiveLockLock(sKextLock);

	if (requestTagOut) {
		*requestTagOut = kOSKextRequestTagInvalid;
	}

	/* If requests to user space are disabled, don't go any further */
	if (!sKernelRequestsEnabled) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "Can't request resource %s for %s - requests to user space are disabled.",
		    resourceNameCString,
		    kextIdentifierCString);
		result = kOSKextReturnDisabled;
		goto finish;
	}

	if (!kextIdentifierCString || !resourceNameCString || !callback) {
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	callbackKext = OSKext::lookupKextWithAddress((vm_address_t)callback);
	if (!callbackKext) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "Resource request has bad callback address.");
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}
	if (!callbackKext->flags.starting && !callbackKext->flags.started) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "Resource request callback is in a kext that is not started.");
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	/* Do not allow any new requests to be made on a kext that is unloading.
	 */
	if (callbackKext->flags.stopping) {
		result = kOSKextReturnStopping;
		goto finish;
	}

	/* If we're wrapped the next available request tag around to the negative
	 * numbers, we can't service any more requests.
	 */
	if (sNextRequestTag == kOSKextRequestTagInvalid) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "No more request tags available; restart required.");
		result = kOSKextReturnNoResources;
		goto finish;
	}
	requestTag = sNextRequestTag++;

	result = _OSKextCreateRequest(kKextRequestPredicateRequestResource,
	    requestDict);
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	kextIdentifier = OSString::withCString(kextIdentifierCString);
	resourceName   = OSString::withCString(resourceNameCString);
	requestTagNum  = OSNumber::withNumber((long long unsigned int)requestTag,
	    8 * sizeof(requestTag));
	if (!kextIdentifier ||
	    !resourceName ||
	    !requestTagNum ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentBundleIdentifierKey, kextIdentifier.get()) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentNameKey, resourceName.get()) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentRequestTagKey, requestTagNum.get())) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	callbackRecord = OSDynamicPtrCast<OSDictionary>(requestDict->copyCollection());
	if (!callbackRecord) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}
	// we validate callback address at call time
	callbackWrapper = OSValueObjectWithValue(callback);
	if (context) {
		contextWrapper = OSValueObjectWithValue(context);
	}
	if (!callbackWrapper || !_OSKextSetRequestArgument(callbackRecord.get(),
	    kKextRequestArgumentCallbackKey, callbackWrapper.get())) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	if (context) {
		if (!contextWrapper || !_OSKextSetRequestArgument(callbackRecord.get(),
		    kKextRequestArgumentContextKey, contextWrapper.get())) {
			result = kOSKextReturnNoMemory;
			goto finish;
		}
	}

	/* Only post the requests after all the other potential failure points
	 * have been passed.
	 */
	if (!sKernelRequests->setObject(requestDict.get()) ||
	    !sRequestCallbackRecords->setObject(callbackRecord.get())) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	OSKext::pingIOKitDaemon();

	result = kOSReturnSuccess;
	if (requestTagOut) {
		*requestTagOut = requestTag;
	}

finish:

	/* If we didn't succeed, yank the request & callback
	 * from their holding arrays.
	 */
	if (result != kOSReturnSuccess) {
		unsigned int index;

		index = sKernelRequests->getNextIndexOfObject(requestDict.get(), 0);
		if (index != (unsigned int)-1) {
			sKernelRequests->removeObject(index);
		}
		index = sRequestCallbackRecords->getNextIndexOfObject(callbackRecord.get(), 0);
		if (index != (unsigned int)-1) {
			sRequestCallbackRecords->removeObject(index);
		}
	}

	OSKext::considerUnloads(/* rescheduleOnly? */ true);

	IORecursiveLockUnlock(sKextLock);

	return result;
}

OSReturn
OSKext::requestDaemonLaunch(
	OSString *kextIdentifier,
	OSString *serverName,
	OSNumber *serverTag,
	OSBoolean *reslide,
	IOUserServerCheckInToken * checkInToken,
	OSData *serverDUI)
{
	OSReturn       result        = kOSReturnError;
	OSSharedPtr<OSDictionary> requestDict;
	unsigned int size = 0;
	const char *dextUniqueIDCString = NULL;

	if (!kextIdentifier || !serverName || !serverTag || !checkInToken) {
		return kOSKextReturnInvalidArgument;
	}
	if (!iokitDaemonAvailable()) {
		panic("Received unexpected request in environment where " kIOKitDaemonName " is unavailable");
	}

	if (serverDUI != NULL) {
		dextUniqueIDCString = getDextUniqueIDCString(serverDUI, &size);
	}

	IORecursiveLockLock(sKextLock);

	OSKextLog(/* kext */ NULL,
	    kOSKextLogDebugLevel |
	    kOSKextLogGeneralFlag,
	    "Requesting daemon launch for %s %s with serverName %s and tag %llu%s",
	    kextIdentifier->getCStringNoCopy(),
	    (dextUniqueIDCString != NULL)?dextUniqueIDCString:"",
	    serverName->getCStringNoCopy(),
	    serverTag->unsigned64BitValue(),
	    reslide == kOSBooleanTrue ? " with reslid shared cache" : ""
	    );

	result = _OSKextCreateRequest(kKextRequestPredicateRequestDaemonLaunch, requestDict);
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	if (!_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentBundleIdentifierKey, kextIdentifier) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentDriverExtensionServerName, serverName) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentDriverExtensionServerTag, serverTag) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentDriverExtensionReslideSharedCache, reslide) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentCheckInToken, checkInToken)) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	if (serverDUI) {
		if (!_OSKextSetRequestArgument(requestDict.get(),
		    kOSBundleDextUniqueIdentifierKey, serverDUI)) {
			result = kOSKextReturnNoMemory;
			goto finish;
		}
	}

	/* Only post the requests after all the other potential failure points
	 * have been passed.
	 */
	if (!sKernelRequests->setObject(requestDict.get())) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}
	result = OSKext::pingIOKitDaemon();
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	result = kOSReturnSuccess;
finish:
	IORecursiveLockUnlock(sKextLock);
	if (dextUniqueIDCString) {
		kfree_data(dextUniqueIDCString, size);
	}
	return result;
}

OSReturn
OSKext::notifyDextUpgrade(
	OSString *kextIdentifier,
	OSData *dextUniqueIdentifier)
{
	OSReturn result = kOSReturnError;
	OSSharedPtr<OSDictionary> requestDict;
	unsigned int size = 0;
	const char *dextUniqueIDCString = getDextUniqueIDCString(dextUniqueIdentifier, &size);
	assert(dextUniqueIDCString != NULL);

	IORecursiveLockLock(sKextLock);

	OSKextLog(NULL,
	    kOSKextLogDebugLevel |
	    kOSKextLogGeneralFlag,
	    "Notifying of dext upgrade for %s with UniqueID %s",
	    kextIdentifier->getCStringNoCopy(), dextUniqueIDCString);

	result = _OSKextCreateRequest(kKextRequestPredicateRequestDaemonUpgradeNotification, requestDict);
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	if (!_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentBundleIdentifierKey, kextIdentifier) ||
	    !_OSKextSetRequestArgument(requestDict.get(),
	    kKextRequestArgumentDriverUniqueIdentifier, dextUniqueIdentifier)) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}

	/* Only post the requests after all the other potential failure points
	 * have been passed.
	 */
	if (!sKernelRequests->setObject(requestDict.get())) {
		result = kOSKextReturnNoMemory;
		goto finish;
	}
	OSKext::pingIOKitDaemon();

	result = kOSReturnSuccess;
finish:
	IORecursiveLockUnlock(sKextLock);

	if (dextUniqueIDCString != NULL) {
		kfree_data(dextUniqueIDCString, size);
	}
	return result;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::dequeueCallbackForRequestTag(
	OSKextRequestTag    requestTag,
	OSSharedPtr<OSDictionary>     &callbackRecordOut)
{
	OSDictionary * callbackRecordOutRaw = NULL;
	OSReturn result;

	result = dequeueCallbackForRequestTag(requestTag,
	    &callbackRecordOutRaw);

	if (kOSReturnSuccess == result) {
		callbackRecordOut.reset(callbackRecordOutRaw, OSNoRetain);
	}

	return result;
}
OSReturn
OSKext::dequeueCallbackForRequestTag(
	OSKextRequestTag    requestTag,
	OSDictionary     ** callbackRecordOut)
{
	OSReturn   result = kOSReturnError;
	OSSharedPtr<OSNumber> requestTagNum;

	requestTagNum  = OSNumber::withNumber((long long unsigned int)requestTag,
	    8 * sizeof(requestTag));
	if (!requestTagNum) {
		goto finish;
	}

	result = OSKext::dequeueCallbackForRequestTag(requestTagNum.get(),
	    callbackRecordOut);

finish:
	return result;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::dequeueCallbackForRequestTag(
	OSNumber     *    requestTagNum,
	OSSharedPtr<OSDictionary>     &callbackRecordOut)
{
	OSDictionary * callbackRecordOutRaw = NULL;
	OSReturn result;

	result = dequeueCallbackForRequestTag(requestTagNum,
	    &callbackRecordOutRaw);

	if (kOSReturnSuccess == result) {
		callbackRecordOut.reset(callbackRecordOutRaw, OSNoRetain);
	}

	return result;
}
OSReturn
OSKext::dequeueCallbackForRequestTag(
	OSNumber     *    requestTagNum,
	OSDictionary ** callbackRecordOut)
{
	OSReturn        result          = kOSKextReturnInvalidArgument;
	OSDictionary  * callbackRecord  = NULL;        // retain if matched!
	OSNumber      * callbackTagNum  = NULL;        // do not release
	unsigned int    count, i;

	result = kOSReturnError;
	count = sRequestCallbackRecords->getCount();
	for (i = 0; i < count; i++) {
		callbackRecord = OSDynamicCast(OSDictionary,
		    sRequestCallbackRecords->getObject(i));
		if (!callbackRecord) {
			goto finish;
		}

		/* If we don't find a tag, we basically have a leak here. Maybe
		 * we should just remove it.
		 */
		callbackTagNum = OSDynamicCast(OSNumber, _OSKextGetRequestArgument(
			    callbackRecord, kKextRequestArgumentRequestTagKey));
		if (!callbackTagNum) {
			goto finish;
		}

		/* We could be even more paranoid and check that all the incoming
		 * args match what's in the callback record.
		 */
		if (callbackTagNum->isEqualTo(requestTagNum)) {
			if (callbackRecordOut) {
				*callbackRecordOut = callbackRecord;
				callbackRecord->retain();
			}
			sRequestCallbackRecords->removeObject(i);
			result = kOSReturnSuccess;
			goto finish;
		}
	}
	result = kOSKextReturnNotFound;

finish:
	return result;
}


/*********************************************************************
* Busy timeout triage
*********************************************************************/
/* static */
bool
OSKext::pendingIOKitDaemonRequests(void)
{
	return sRequestCallbackRecords && sRequestCallbackRecords->getCount();
}

/*********************************************************************
* Acquires and releases sKextLock
*
* This function is designed to be called by kernelmanagerd and driverkitd
* and it gathers all codeless kext and dext personalities, and then attempts
* to map a System (pageable) KC and an Auxiliary (aux) KC.
*
* The pageable and aux KC can be loaded only once at boot time.
* Even if the pageable or aux KC fail to load - this function will
* not allow a new pageable or aux KC to be installed by subsequent calls.
* This is done to avoid security issues where userspace has been compromised
* or the pageable kc has been tampered with and the attacker
* attempts to re-load a malicious variant.
* However dexts can be dynamically loaded, so this function can be used
* to request the installation of a new set of dexts even after boot time.
*
*
*
* Return: if a KC fails to load the return value will contain:
*         kOSKextReturnKCLoadFailure. If the pageable KC fails,
*         the return value will contain kOSKextReturnKCLoadFailureSystemKC.
*         Similarly, if the aux kc load fails, the return value will
*         contain kOSKextReturnKCLoadFailureAuxKC. The two values
*         compose with each other and with kOSKextReturnKCLoadFailure.
*********************************************************************/
/* static */
OSReturn
OSKext::loadFileSetKexts(OSDictionary * requestDict __unused)
{
	static bool daemon_ready = false;

	OSReturn ret = kOSKextReturnInvalidArgument;
	OSReturn kcerr = 0;
	bool start_matching = false;

	bool allow_fileset_load = !daemon_ready;
#if !(defined(__x86_64__) || defined(__i386__))
	/* never allow KCs full of kexts on non-x86 machines */
	allow_fileset_load = false;
#endif

	/*
	 * Change with 70582300
	 */
#if 0 || !defined(VM_MAPPED_KEXTS)
	/*
	 * On platforms that don't support the SystemKC or a file-backed
	 * AuxKC, the kext receipt for 3rd party kexts loaded by the booter
	 * needs to be queried before we load any codeless kexts or release
	 * any 3rd party kexts to run. On platforms that support a file-backed
	 * AuxKC, this process is done via the kext audit mechanism.
	 */

	printf("KextLog: waiting for kext receipt to be queried.\n");
	while (!IOServiceWaitForMatchingResource(kOSKextReceiptQueried, UINT64_MAX)) {
		IOSleep(30);
	}
#endif /* !VM_MAPPED_KEXTS */

	/*
	 * Get the args from the request. Right now we need the file
	 * name for the pageable and the aux kext collection file sets.
	 */
	OSDictionary * requestArgs                = NULL;        // do not release
	OSString     * pageable_filepath          = NULL;        // do not release
	OSString     * aux_filepath               = NULL;        // do not release
	OSArray      * codeless_kexts             = NULL;        // do not release

	kernel_mach_header_t *akc_mh              = NULL;

	requestArgs = OSDynamicCast(OSDictionary,
	    requestDict->getObject(kKextRequestArgumentsKey));

	if (requestArgs == NULL) {
		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "KextLog: No arguments in plist for loading fileset kext\n");
		printf("KextLog: No arguments in plist for loading fileset kext\n");
		return ret;
	}

	ret = kOSKextReturnDisabled;

	IORecursiveLockLock(sKextLock);

	if (!sLoadEnabled) {
		OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "KextLog: Kext loading is disabled (attempt to load KCs).");
		IORecursiveLockUnlock(sKextLock);
		return ret;
	}

	pageable_filepath = OSDynamicCast(OSString,
	    requestArgs->getObject(kKextRequestArgumentPageableKCFilename));

	if (allow_fileset_load && pageable_filepath != NULL) {
		printf("KextLog: Loading Pageable KC from file %s\n", pageable_filepath->getCStringNoCopy());

		ret = OSKext::loadKCFileSet(pageable_filepath->getCStringNoCopy(), KCKindPageable);
		if (ret) {
			OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
			    "KextLog: loadKCFileSet for Pageable KC returned %d\n", ret);

			printf("KextLog: loadKCFileSet for Pageable KC returned %d\n", ret);
			ret = kOSKextReturnKCLoadFailure;
			kcerr |= kOSKextReturnKCLoadFailureSystemKC;
			goto try_auxkc;
		}
		/*
		 * Even if the AuxKC fails to load, we still want to send
		 * the System KC personalities to the catalog for matching
		 */
		start_matching = true;
	} else if (pageable_filepath != NULL) {
		OSKextLog(/* kext */ NULL, kOSKextLogBasicLevel | kOSKextLogIPCFlag,
		    "KextLog: ignoring Pageable KC load from %s\n", pageable_filepath->getCStringNoCopy());
		ret = kOSKextReturnUnsupported;
	}

try_auxkc:
	akc_mh = (kernel_mach_header_t*)PE_get_kc_header(KCKindAuxiliary);
	if (akc_mh) {
		/*
		 * If we try to load a deferred AuxKC, then don't ever attempt
		 * a filesystem map of a file
		 */
		allow_fileset_load = false;

		/*
		 * This function is only called once per boot, so we haven't
		 * yet loaded an AuxKC. If we have registered the AuxKC mach
		 * header, that means that the kext collection has been placed
		 * in memory for us by the booter, and is waiting for us to
		 * process it.  Grab the deferred XML plist of info
		 * dictionaries and add all the kexts.
		 */
		OSSharedPtr<OSObject>  parsedXML;
		OSSharedPtr<OSData>    loaded_kcUUID;
		OSDictionary          *infoDict;
		parsedXML = consumeDeferredKextCollection(KCKindAuxiliary);
		infoDict = OSDynamicCast(OSDictionary, parsedXML.get());
#if !defined(VM_MAPPED_KEXTS)
		/*
		 * On platforms where we don't dynamically wire-down / page-in
		 * kext memory, we need to maintain the invariant that if the
		 * AuxKC in memory does not contain a kext receipt, then we
		 * should not load any of the kexts.
		 */
		size_t receipt_sz = 0;
		if (getsectdatafromheader(akc_mh, kReceiptInfoSegment, kAuxKCReceiptSection, &receipt_sz) == NULL || receipt_sz == 0) {
			OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
			    "KextLog: WARNING: Failed to load AuxKC from memory: missing receipt");
			ret = kOSKextReturnKCLoadFailure;
			goto try_codeless;
		}
#endif
		if (infoDict) {
			bool added;
			printf("KextLog: Adding kexts from in-memory AuxKC\n");
			added = OSKext::addKextsFromKextCollection(akc_mh, infoDict,
			    kPrelinkTextSegment, loaded_kcUUID, KCKindAuxiliary);
			if (!loaded_kcUUID) {
				OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
				    "KextLog: WARNING: did not find UUID in deferred Aux KC!");
			} else if (!added) {
				OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
				    "KextLog: WARNING: Failed to load AuxKC from memory.");
			}
			/* only return success if the pageable load (above) was successful */
			if (ret != kOSKextReturnKCLoadFailure) {
				ret = kOSReturnSuccess;
			}
			/* the registration of the AuxKC parsed out the KC's UUID already */
		} else {
			if (daemon_ready) {
				/*
				 * Complain, but don't return an error if this isn't the first time the
				 * IOKit daemon is checking in. If the daemon ever restarts, we will
				 * hit this case because we've already consumed the deferred personalities.
				 * We return success here so that a call to this function from a restarted
				 * daemon with no codeless kexts will succeed.
				 */
				OSKextLog(/* kext */ NULL, kOSKextLogBasicLevel | kOSKextLogIPCFlag,
				    "KextLog: can't re-parse deferred AuxKC personalities on IOKit daemon restart");
				if (ret != kOSKextReturnKCLoadFailure) {
					ret = kOSReturnSuccess;
				}
			} else {
				/* this is a real error case */
				OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogIPCFlag,
				    "KextLog: ERROR loading deferred AuxKC: PRELINK_INFO wasn't an OSDictionary");
				printf("KextLog: ERROR loading deferred AuxKC: PRELINK_INFO wasn't an OSDictionary\n");
				ret = kOSKextReturnKCLoadFailure;
				kcerr |= kOSKextReturnKCLoadFailureAuxKC;
			}
		}
	}

	aux_filepath = OSDynamicCast(OSString,
	    requestArgs->getObject(kKextRequestArgumentAuxKCFilename));
	if (allow_fileset_load && aux_filepath != NULL) {
		printf("KextLog: Loading Aux KC from file %s\n", aux_filepath->getCStringNoCopy());

		ret = OSKext::loadKCFileSet(aux_filepath->getCStringNoCopy(), KCKindAuxiliary);
		if (ret) {
			OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
			    "KextLog: loadKCFileSet for Aux KC returned %d\n", ret);

			printf("KextLog: loadKCFileSet for Aux KC returned %d\n", ret);
			ret = kOSKextReturnKCLoadFailure;
			kcerr |= kOSKextReturnKCLoadFailureAuxKC;
			goto try_codeless;
		}
		start_matching = true;
	} else if (aux_filepath != NULL) {
		OSKextLog(/* kext */ NULL, kOSKextLogBasicLevel | kOSKextLogIPCFlag,
		    "KextLog: Ignoring AuxKC load from %s\n", aux_filepath->getCStringNoCopy());
		if (ret != kOSKextReturnKCLoadFailure) {
			ret = kOSKextReturnUnsupported;
		}
	}

try_codeless:
	/*
	 * Load codeless kexts last so that there is no possibilty of a
	 * codeless kext bundle ID preventing a kext in the system KC from
	 * loading
	 */
	codeless_kexts = OSDynamicCast(OSArray,
	    requestArgs->getObject(kKextRequestArgumentCodelessPersonalities));
	if (codeless_kexts != NULL) {
		uint32_t count = codeless_kexts->getCount();
		OSKextLog(NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "KextLog: loading %d codeless kexts/dexts", count);
		for (uint32_t i = 0; i < count; i++) {
			OSDictionary *infoDict;
			infoDict = OSDynamicCast(OSDictionary,
			    codeless_kexts->getObject(i));
			if (!infoDict) {
				continue;
			}
			// instantiate a new kext, and don't hold a reference
			// (the kext subsystem will hold one implicitly)
			OSKext::withCodelessInfo(infoDict, NULL);
		}
		/* ignore errors that are not KC load failures */
		if (ret != kOSKextReturnKCLoadFailure) {
			ret = kOSReturnSuccess;
		}
		start_matching = true;
	}

	/* send personalities to the IOCatalog once */
	if (ret == kOSReturnSuccess || start_matching || sOSKextWasResetAfterUserspaceReboot) {
		OSKext::sendAllKextPersonalitiesToCatalog(true);
		/*
		 * This request necessarily came from the IOKit daemon (kernelmanagerd), so mark
		 * things as active and start all the delayed matching: the
		 * dext and codeless kext personalities should have all been
		 * delivered via this one call.
		 */
		if (!daemon_ready) {
			OSKext::setIOKitDaemonActive();
			OSKext::setDeferredLoadSucceeded(TRUE);
			IOService::iokitDaemonLaunched();
		}
		if (sOSKextWasResetAfterUserspaceReboot) {
			sOSKextWasResetAfterUserspaceReboot = false;
			OSKext::setIOKitDaemonActive();
			IOService::startDeferredMatches();
		}
	}

	if (ret == kOSKextReturnKCLoadFailure) {
		ret |= kcerr;
	}

	/*
	 * Only allow this function to attempt to load the pageable and
	 * aux KCs once per boot.
	 */
	daemon_ready = true;

	IORecursiveLockUnlock(sKextLock);

	return ret;
}

OSReturn
OSKext::resetMutableSegments(void)
{
	kernel_segment_command_t *seg = NULL;
	kernel_mach_header_t *k_mh = (kernel_mach_header_t *)kmod_info->address;
	u_int index = 0;
	OSKextSavedMutableSegment *savedSegment = NULL;
	uintptr_t kext_slide = PE_get_kc_slide(kc_type);
	OSReturn err;

	if (!savedMutableSegments) {
		OSKextLog(this, kOSKextLogErrorLevel | kOSKextLogLoadFlag,
		    "Kext %s cannot be reset, mutable segments were not saved.", getIdentifierCString());
		err = kOSKextReturnInternalError;
		goto finish;
	}

	for (seg = firstsegfromheader(k_mh), index = 0; seg; seg = nextsegfromheader(k_mh, seg)) {
		if (!segmentIsMutable(seg)) {
			continue;
		}
		uint64_t unslid_vmaddr = seg->vmaddr - kext_slide;
		uint64_t vmsize = seg->vmsize;
		err = kOSKextReturnInternalError;
		for (index = 0; index < savedMutableSegments->getCount(); index++) {
			savedSegment = OSDynamicCast(OSKextSavedMutableSegment, savedMutableSegments->getObject(index));
			assert(savedSegment);
			if (savedSegment->getVMAddr() == seg->vmaddr && savedSegment->getVMSize() == seg->vmsize) {
				OSKextLog(this, kOSKextLogDebugLevel | kOSKextLogLoadFlag,
				    "Resetting kext %s, mutable segment %.*s %llx->%llx.", getIdentifierCString(), (int)strnlen(seg->segname, sizeof(seg->segname)), seg->segname, unslid_vmaddr, unslid_vmaddr + vmsize - 1);
				err = savedSegment->restoreContents(seg);
				if (err != kOSReturnSuccess) {
					panic("Kext %s cannot be reset, mutable segment %llx->%llx could not be restored.", getIdentifierCString(), unslid_vmaddr, unslid_vmaddr + vmsize - 1);
				}
			}
		}
		if (err != kOSReturnSuccess) {
			panic("Kext %s cannot be reset, could not find saved mutable segment for %llx->%llx.", getIdentifierCString(), unslid_vmaddr, unslid_vmaddr + vmsize - 1);
		}
	}
	err = kOSReturnSuccess;
finish:
	return err;
}


/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::loadKCFileSet(
	const char *filepath,
	kc_kind_t   type)
{
#if VM_MAPPED_KEXTS
	/* we only need to load filesets on systems that support VM_MAPPED kexts */
	OSReturn err;
	struct vnode *vp = NULL;
	void *fileset_control;
	off_t fsize;
	bool pageable = (type == KCKindPageable);

	if ((pageable && pageableKCloaded) ||
	    (!pageable && auxKCloaded)) {
		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "KC FileSet of type %s is already loaded", (pageable ? "Pageable" : "Aux"));

		return kOSKextReturnInvalidArgument;
	}

	/* Do not allow AuxKC to load if Pageable KC is not loaded */
	if (!pageable && !pageableKCloaded) {
		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "Trying to load the Aux KC without loading the Pageable KC");
		return kOSKextReturnInvalidArgument;
	}

	fileset_control = ubc_getobject_from_filename(filepath, &vp, &fsize);

	if (fileset_control == NULL) {
		printf("Could not get memory control object for file %s", filepath);

		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "Could not get memory control object for file %s", filepath);
		return kOSKextReturnInvalidArgument;
	}
	if (vp == NULL) {
		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "Could not find vnode for file %s", filepath);
		return kOSKextReturnInvalidArgument;
	}

	kernel_mach_header_t *mh = NULL;
	uintptr_t slide = 0;

#if CONFIG_CSR
	/*
	 * When SIP is enabled, the KC we map must be SIP-protected
	 */
	if (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0) {
		struct vnode_attr va;
		int error;
		VATTR_INIT(&va);
		VATTR_WANTED(&va, va_flags);
		error = vnode_getattr(vp, &va, vfs_context_current());
		if (error) {
			OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
			    "vnode_getattr(%s) failed (error=%d)", filepath, error);
			err = kOSKextReturnInternalError;
			goto finish;
		}
		if (!(va.va_flags & SF_RESTRICTED)) {
			OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
			    "Path to KC '%s' is not SIP-protected", filepath);
			err = kOSKextReturnInvalidArgument;
			goto finish;
		}
	}
#endif

	err = OSKext::mapKCFileSet(fileset_control, (vm_size_t)fsize, &mh, 0, &slide, pageable, NULL);
	if (err) {
		printf("KextLog: mapKCFileSet returned %d\n", err);

		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "mapKCFileSet returned %d\n", err);

		err = kOSKextReturnInvalidArgument;
	}

#if CONFIG_CSR
finish:
#endif
	/* Drop the vnode ref returned by ubc_getobject_from_filename if mapKCFileSet failed */
	assert(vp != NULL);
	if (err == kOSReturnSuccess) {
		PE_set_kc_vp(type, vp);
		if (pageable) {
			pageableKCloaded = true;
		} else {
			auxKCloaded = true;
		}
	} else {
		vnode_put(vp);
	}

	return err;
#else
	(void)filepath;
	(void)type;
	return kOSKextReturnUnsupported;
#endif // VM_MAPPED_KEXTS
}

#if defined(__x86_64__) || defined(__i386__)
/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::mapKCFileSet(
	void                 *control,
	vm_size_t            fsize,
	kernel_mach_header_t **mhp,
	off_t                file_offset,
	uintptr_t            *slidep,
	bool                 pageable,
	void                 *map_entry_list)
{
	bool fileset_load = false;
	kern_return_t ret;
	OSReturn err;
	kernel_section_t *infoPlistSection = NULL;
	OSDictionary *infoDict = NULL;

	OSSharedPtr<OSObject> parsedXML;
	OSSharedPtr<OSString> errorString;
	OSSharedPtr<OSData> loaded_kcUUID;

	/* Check if initial load for file set */
	if (*mhp == NULL) {
		fileset_load = true;

		/* Get a page aligned address from kext map to map the file */
		vm_map_offset_t pagealigned_addr = get_address_from_kext_map(fsize);
		if (pagealigned_addr == 0) {
			return kOSKextReturnNoMemory;
		}

		*mhp = (kernel_mach_header_t *)pagealigned_addr;

		/* Allocate memory for bailout mechanism */
		map_entry_list = allocate_kcfileset_map_entry_list();
		if (map_entry_list == NULL) {
			return kOSKextReturnNoMemory;
		}
	}

	uintptr_t *slideptr = fileset_load ? slidep : NULL;
	err = mapKCTextSegment(control, mhp, file_offset, slideptr, map_entry_list);
	/* mhp and slideptr are updated by mapKCTextSegment */
	if (err) {
		if (fileset_load) {
			deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, TRUE, pageable);
		}
		return err;
	}

	/* Initialize the kc header globals */
	if (fileset_load) {
		if (pageable) {
			PE_set_kc_header(KCKindPageable, *mhp, *slidep);
		} else {
			PE_set_kc_header(KCKindAuxiliary, *mhp, *slidep);
		}
	}

	/* Iterate through all the segments and map necessary segments */
	struct load_command *lcp = (struct load_command *) (*mhp + 1);
	for (unsigned int i = 0; i < (*mhp)->ncmds; i++, lcp = (struct load_command *)((uintptr_t)lcp + lcp->cmdsize)) {
		vm_map_offset_t start;
		kernel_mach_header_t *k_mh = NULL;
		kernel_segment_command_t * seg = NULL;
		struct fileset_entry_command *fse = NULL;

		if (lcp->cmd == LC_SEGMENT_KERNEL) {
			seg = (kernel_segment_command_t *)lcp;
			start = ((uintptr_t)(seg->vmaddr)) + *slidep;
		} else if (lcp->cmd == LC_FILESET_ENTRY) {
			fse = (struct fileset_entry_command *)lcp;
			k_mh = (kernel_mach_header_t *)(((uintptr_t)(fse->vmaddr)) + *slidep);

			/* Map the segments of the mach-o binary */
			err = OSKext::mapKCFileSet(control, 0, &k_mh, fse->fileoff, slidep, pageable, map_entry_list);
			if (err) {
				deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, TRUE, pageable);
				return kOSKextReturnInvalidArgument;
			}
			continue;
		} else if (lcp->cmd == LC_DYLD_CHAINED_FIXUPS) {
			/* Check if the Aux KC is built pageable style */
			if (!pageable && !fileset_load && !auxKCloaded) {
				resetAuxKCSegmentOnUnload = true;
			}
			continue;
		} else {
			continue;
		}

		if (fileset_load) {
			if (seg->vmsize == 0) {
				continue;
			}

			/* Only map __PRELINK_INFO, __BRANCH_STUBS, __BRANCH_GOTS and __LINKEDIT sections */
			if (strncmp(seg->segname, kPrelinkInfoSegment, sizeof(seg->segname)) != 0 &&
			    strncmp(seg->segname, kKCBranchStubs, sizeof(seg->segname)) != 0 &&
			    strncmp(seg->segname, kKCBranchGots, sizeof(seg->segname)) != 0 &&
			    strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)) != 0) {
				continue;
			}
		} else {
			if (seg->vmsize == 0) {
				continue;
			}

			/* Skip the __LINKEDIT, __LINKINFO and __TEXT segments */
			if (strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)) == 0 ||
			    strncmp(seg->segname, SEG_LINKINFO, sizeof(seg->segname)) == 0 ||
			    strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname)) == 0) {
				continue;
			}
		}

		ret = vm_map_kcfileset_segment(
			&start, seg->vmsize,
			(memory_object_control_t)control, seg->fileoff, seg->maxprot);

		if (ret != KERN_SUCCESS) {
			if (fileset_load) {
				deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, TRUE, pageable);
			}
			return kOSKextReturnInvalidArgument;
		}
		add_kcfileset_map_entry(map_entry_list, start, seg->vmsize);
	}

	/* Return if regular mach-o */
	if (!fileset_load) {
		return 0;
	}

	/*
	 * Fixup for the Pageable KC and the Aux KC is done by
	 * i386_slide_kext_collection_mh_addrs, but it differs in
	 * following ways:
	 *
	 * PageableKC: Fixup only __BRANCH_STUBS segment and top level load commands.
	 * The fixup of kext segments and kext load commands are done at kext
	 * load time by calling i386_slide_individual_kext.
	 *
	 * AuxKC old style: Fixup all the segments and all the load commands.
	 *
	 * AuxKC pageable style: Same as the Pageable KC.
	 */
	bool adjust_mach_header = (pageable ? true : ((resetAuxKCSegmentOnUnload) ? true : false));
	ret = i386_slide_kext_collection_mh_addrs(*mhp, *slidep, adjust_mach_header);
	if (ret != KERN_SUCCESS) {
		deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, TRUE, pageable);
		return kOSKextReturnInvalidArgument;
	}

	/* Get the prelink info dictionary */
	infoPlistSection = getsectbynamefromheader(*mhp, kPrelinkInfoSegment, kPrelinkInfoSection);
	parsedXML = OSUnserializeXML((const char *)infoPlistSection->addr, errorString);
	if (parsedXML) {
		infoDict = OSDynamicCast(OSDictionary, parsedXML.get());
	}

	if (!infoDict) {
		const char *errorCString = "(unknown error)";

		if (errorString && errorString->getCStringNoCopy()) {
			errorCString = errorString->getCStringNoCopy();
		} else if (parsedXML) {
			errorCString = "not a dictionary";
		}
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "Error unserializing kext info plist section: %s.", errorCString);
		deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, TRUE, pageable);
		return kOSKextReturnInvalidArgument;
	}

	/* Validate that the Kext Collection is prelinked to the loaded KC */
	err = OSKext::validateKCFileSetUUID(infoDict, pageable ? KCKindPageable : KCKindAuxiliary);
	if (err) {
		deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, TRUE, pageable);
		return kOSKextReturnInvalidArgument;
	}

	/* Set Protection of Segments */
	OSKext::protectKCFileSet(*mhp, pageable ? KCKindPageable : KCKindAuxiliary);

	OSKext::addKextsFromKextCollection(*mhp,
	    infoDict, kPrelinkTextSegment,
	    loaded_kcUUID, pageable ? KCKindPageable : KCKindAuxiliary);

	/* Copy in the KC UUID */
	if (!loaded_kcUUID) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "WARNING: did not find UUID in prelinked %s KC!", pageable ? "Pageable" : "Aux");
	} else if (pageable) {
		pageablekc_uuid_valid = TRUE;
		memcpy((void *)&pageablekc_uuid, (const void *)loaded_kcUUID->getBytesNoCopy(), loaded_kcUUID->getLength());
		uuid_unparse_upper(pageablekc_uuid, pageablekc_uuid_string);
	} else {
		auxkc_uuid_valid = TRUE;
		memcpy((void *)&auxkc_uuid, (const void *)loaded_kcUUID->getBytesNoCopy(), loaded_kcUUID->getLength());
		uuid_unparse_upper(auxkc_uuid, auxkc_uuid_string);
	}

	deallocate_kcfileset_map_entry_list_and_unmap_entries(map_entry_list, FALSE, pageable);

	return 0;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::mapKCTextSegment(
	void                 *control,
	kernel_mach_header_t **mhp,
	off_t                file_offset,
	uintptr_t            *slidep,
	void                 *map_entry_list)
{
	kern_return_t ret;
	vm_map_offset_t mach_header_map_size = vm_map_round_page(sizeof(kernel_mach_header_t),
	    PAGE_MASK);
	vm_map_offset_t load_command_map_size = 0;
	kernel_mach_header_t *base_mh = *mhp;

	/* Map the mach header at start of fileset for now (vmaddr = 0) */
	ret = vm_map_kcfileset_segment(
		(vm_map_offset_t *)&base_mh, mach_header_map_size,
		(memory_object_control_t)control, file_offset, (VM_PROT_READ | VM_PROT_WRITE));

	if (ret != KERN_SUCCESS) {
		printf("Kext Log: mapKCTextSegment failed to map mach header of fileset %x", ret);

		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "Failed to map mach header of kc fileset with error %d", ret);
		return kOSKextReturnInvalidArgument;
	}

	if (slidep) {
		/* Verify that it's an MH_FILESET */
		if (base_mh->filetype != MH_FILESET) {
			printf("Kext Log: mapKCTextSegment mach header filetype"
			    " is not an MH_FILESET, it is %x", base_mh->filetype);

			OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
			    "mapKCTextSegment mach header filetype is not an MH_FILESET, it is %x", base_mh->filetype);

			/* Unmap the mach header */
			vm_unmap_kcfileset_segment((vm_map_offset_t *)&base_mh, mach_header_map_size);
			return kOSKextReturnInvalidArgument;
		}
	}

	/* Map the remaining pages of load commands */
	if (base_mh->sizeofcmds > mach_header_map_size) {
		vm_map_offset_t load_command_addr = ((vm_map_offset_t)base_mh) + mach_header_map_size;
		load_command_map_size = base_mh->sizeofcmds - mach_header_map_size;

		/* Map the load commands */
		ret = vm_map_kcfileset_segment(
			&load_command_addr, load_command_map_size,
			(memory_object_control_t)control, file_offset + mach_header_map_size,
			(VM_PROT_READ | VM_PROT_WRITE));

		if (ret != KERN_SUCCESS) {
			printf("KextLog: mapKCTextSegment failed to map load commands of fileset %x", ret);
			OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
			    "Failed to map load commands of kc fileset with error %d", ret);

			/* Unmap the mach header */
			vm_unmap_kcfileset_segment((vm_map_offset_t *)&base_mh, mach_header_map_size);
			return kOSKextReturnInvalidArgument;
		}
	}

	kernel_segment_command_t *text_seg;
	text_seg = getsegbynamefromheader((kernel_mach_header_t *)base_mh, SEG_TEXT);

	/* Calculate the slide and vm addr of mach header */
	if (slidep) {
		*mhp = (kernel_mach_header_t *)((uintptr_t)base_mh + text_seg->vmaddr);
		*slidep = ((uintptr_t)*mhp) - text_seg->vmaddr;
	}

	/* Cache the text segment size and file offset before unmapping */
	vm_map_offset_t text_segment_size = text_seg->vmsize;
	vm_object_offset_t text_segment_fileoff = text_seg->fileoff;
	vm_prot_t text_maxprot = text_seg->maxprot;

	/* Unmap the first page and loadcommands and map the text segment */
	ret = vm_unmap_kcfileset_segment((vm_map_offset_t *)&base_mh, mach_header_map_size);
	assert(ret == KERN_SUCCESS);

	if (load_command_map_size) {
		vm_map_offset_t load_command_addr = ((vm_map_offset_t)base_mh) + mach_header_map_size;
		ret = vm_unmap_kcfileset_segment(&load_command_addr, load_command_map_size);
		assert(ret == KERN_SUCCESS);
	}

	/* Map the text segment at actual vm addr specified in fileset */
	ret = vm_map_kcfileset_segment((vm_map_offset_t *)mhp, text_segment_size,
	    (memory_object_control_t)control, text_segment_fileoff, text_maxprot);
	if (ret != KERN_SUCCESS) {
		OSKextLog(/* kext */ NULL, kOSKextLogDebugLevel | kOSKextLogIPCFlag,
		    "Failed to map Text segment of kc fileset with error %d", ret);
		return kOSKextReturnInvalidArgument;
	}

	add_kcfileset_map_entry(map_entry_list, (vm_map_offset_t)*mhp, text_segment_size);
	return 0;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::protectKCFileSet(
	kernel_mach_header_t *mh,
	kc_kind_t            type)
{
	vm_map_t                    kext_map        = g_kext_map;
	kernel_segment_command_t  * seg             = NULL;
	vm_map_offset_t             start           = 0;
	vm_map_offset_t             end             = 0;
	vm_map_size_t               size            = 0;
	OSReturn                    ret             = 0;

	/* Set VM permissions */
	seg = firstsegfromheader((kernel_mach_header_t *)mh);
	while (seg) {
		start = round_page(seg->vmaddr);
		end = trunc_page(seg->vmaddr + seg->vmsize);
		size = end - start;

		/*
		 * Wire down and protect __TEXT, __BRANCH_STUBS and __BRANCH_GOTS
		 * for the Pageable KC and the Aux KC, wire down and protect __LINKEDIT
		 * for the Aux KC as well.
		 */
		if (strncmp(seg->segname, kKCBranchGots, sizeof(seg->segname)) == 0 ||
		    strncmp(seg->segname, kKCBranchStubs, sizeof(seg->segname)) == 0 ||
		    strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname)) == 0 ||
		    (type == KCKindAuxiliary && !resetAuxKCSegmentOnUnload &&
		    strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)) == 0)) {
			ret = OSKext_protect((kernel_mach_header_t *)mh,
			    kext_map, start, size, seg->maxprot, TRUE, type);
			if (ret != KERN_SUCCESS) {
				printf("OSKext protect failed with error %d", ret);
				return kOSKextReturnInvalidArgument;
			}

			ret = OSKext_protect((kernel_mach_header_t *)mh,
			    kext_map, start, size, seg->initprot, FALSE, type);
			if (ret != KERN_SUCCESS) {
				printf("OSKext protect failed with error %d", ret);
				return kOSKextReturnInvalidArgument;
			}

			ret = OSKext_wire((kernel_mach_header_t *)mh,
			    kext_map, start, end, seg->initprot, FALSE, type);
			if (ret != KERN_SUCCESS) {
				printf("OSKext wire failed with error %d", ret);
				return kOSKextReturnInvalidArgument;
			}
		}

		seg = nextsegfromheader((kernel_mach_header_t *) mh, seg);
	}

	return 0;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
void
OSKext::freeKCFileSetcontrol(void)
{
	PE_reset_all_kc_vp();
}

/*********************************************************************
* Assumes sKextLock is held.
*
* resetKCFileSetSegments: Kext start function expects data segment to
* be pristine on every load, unmap the dirty segments on unload and
* remap them from FileSet on disk. Remap all segments of kext since
* fixups are done per kext and not per segment.
*********************************************************************/
OSReturn
OSKext::resetKCFileSetSegments(void)
{
	kernel_segment_command_t *seg = NULL;
	kernel_segment_command_t *text_seg;
	uint32_t text_fileoff;
	kernel_mach_header_t *k_mh = NULL;
	uintptr_t slide;
	struct vnode *vp = NULL;
	void *fileset_control = NULL;
	bool pageable = (kc_type == KCKindPageable);
	OSReturn err;
	kern_return_t kr;

	/* Check the vnode reference is still available */
	vp = (struct vnode *)PE_get_kc_vp(kc_type);
	if (vp == NULL) {
		OSKextLog(this, kOSKextLogProgressLevel | kOSKextLogLoadFlag,
		    "Kext %s could not be reset, since reboot released the vnode ref", getIdentifierCString());
		return kOSKextReturnInternalError;
	}

	fileset_control = ubc_getobject(vp, 0);
	assert(fileset_control != NULL);

	OSKextLog(this, kOSKextLogProgressLevel | kOSKextLogLoadFlag,
	    "Kext %s resetting all segments", getIdentifierCString());

	k_mh = (kernel_mach_header_t *)kmod_info->address;
	text_seg = getsegbynamefromheader((kernel_mach_header_t *)kmod_info->address, SEG_TEXT);
	text_fileoff = text_seg->fileoff;
	slide = PE_get_kc_slide(kc_type);

	seg = firstsegfromheader((kernel_mach_header_t *)k_mh);
	while (seg) {
		if (seg->vmsize == 0) {
			seg = nextsegfromheader((kernel_mach_header_t *) k_mh, seg);
			continue;
		}

		/* Skip the __LINKEDIT, __LINKINFO and __TEXT segments */
		if (strncmp(seg->segname, SEG_LINKEDIT, sizeof(seg->segname)) == 0 ||
		    strncmp(seg->segname, SEG_LINKINFO, sizeof(seg->segname)) == 0 ||
		    strncmp(seg->segname, SEG_TEXT, sizeof(seg->segname)) == 0) {
			seg = nextsegfromheader((kernel_mach_header_t *) k_mh, seg);
			continue;
		}

		kr = vm_unmap_kcfileset_segment(&seg->vmaddr, seg->vmsize);
		assert(kr == KERN_SUCCESS);
		seg = nextsegfromheader((kernel_mach_header_t *) k_mh, seg);
	}

	/* Unmap the text segment */
	kr = vm_unmap_kcfileset_segment(&text_seg->vmaddr, text_seg->vmsize);
	assert(kr == KERN_SUCCESS);

	/* Map all the segments of the kext */
	err = OSKext::mapKCFileSet(fileset_control, 0, &k_mh, text_fileoff, &slide, pageable, NULL);
	if (err) {
		panic("Could not reset segments of a mapped kext, error %x", err);
	}

	/* Update address in kmod_info, since it has been reset */
	if (kmod_info->address) {
		kmod_info->address = (((uintptr_t)(kmod_info->address)) + slide);
	}

	return 0;
}

/*********************************************************************
* Mechanism to track all segment mapping while mapping KC fileset.
*********************************************************************/

struct kcfileset_map_entry {
	vm_map_offset_t me_start;
	vm_map_offset_t me_size;
};

struct kcfileset_map_entry_list {
	int                        kme_list_count;
	int                        kme_list_index;
	struct kcfileset_map_entry kme_list[];
};

#define KCFILESET_MAP_ENTRY_MAX (16380)

static void *
allocate_kcfileset_map_entry_list(void)
{
	struct kcfileset_map_entry_list *entry_list;

	entry_list = kalloc_type(struct kcfileset_map_entry_list,
	    struct kcfileset_map_entry, KCFILESET_MAP_ENTRY_MAX, Z_WAITOK_ZERO);

	entry_list->kme_list_count = KCFILESET_MAP_ENTRY_MAX;
	entry_list->kme_list_index = 0;
	return entry_list;
}

static void
add_kcfileset_map_entry(
	void            *map_entry_list,
	vm_map_offset_t start,
	vm_map_offset_t size)
{
	if (map_entry_list == NULL) {
		return;
	}

	struct kcfileset_map_entry_list *entry_list = (struct kcfileset_map_entry_list *)map_entry_list;

	if (entry_list->kme_list_index >= entry_list->kme_list_count) {
		panic("Ran out of map kc fileset list");
	}

	entry_list->kme_list[entry_list->kme_list_index].me_start = start;
	entry_list->kme_list[entry_list->kme_list_index].me_size = size;

	entry_list->kme_list_index++;
}

static void
deallocate_kcfileset_map_entry_list_and_unmap_entries(
	void      *map_entry_list,
	boolean_t unmap_entries,
	bool      pageable)
{
	struct kcfileset_map_entry_list *entry_list = (struct kcfileset_map_entry_list *)map_entry_list;

	if (unmap_entries) {
		for (int i = 0; i < entry_list->kme_list_index; i++) {
			kern_return_t ret;
			ret = vm_unmap_kcfileset_segment(
				&(entry_list->kme_list[i].me_start),
				entry_list->kme_list[i].me_size);
			assert(ret == KERN_SUCCESS);
		}

		PE_reset_kc_header(pageable ? KCKindPageable : KCKindAuxiliary);
	}

	kfree_type(struct kcfileset_map_entry_list, struct kcfileset_map_entry,
	    KCFILESET_MAP_ENTRY_MAX, entry_list);
}

/*********************************************************************
* Mechanism to map kext segment.
*********************************************************************/

kern_return_t
vm_map_kcfileset_segment(
	vm_map_offset_t    *start,
	vm_map_offset_t    size,
	void               *control,
	vm_object_offset_t fileoffset,
	vm_prot_t          max_prot)
{
	vm_map_kernel_flags_t vmk_flags = {
		.vmf_fixed = true,
		.vmkf_no_copy_on_read = true,
		.vmkf_cs_enforcement_override = true,
		.vm_tag = VM_KERN_MEMORY_OSKEXT,
	};
	kern_return_t ret;

	/* Add Write to max prot to allow fixups */
	max_prot = max_prot | VM_PROT_WRITE;

	/*
	 * Map the segments from file as COPY mappings to
	 * make sure changes on disk to the file does not affect
	 * mapped segments.
	 */
	ret = vm_map_enter_mem_object_control(
		g_kext_map,
		start,
		size,
		(mach_vm_offset_t)0,
		vmk_flags,
		(memory_object_control_t)control,
		fileoffset,
		TRUE,         /* copy */
		(VM_PROT_READ | VM_PROT_WRITE), max_prot,
		VM_INHERIT_NONE);

	return ret;
}

kern_return_t
vm_unmap_kcfileset_segment(
	vm_map_offset_t    *start,
	vm_map_offset_t    size)
{
	return mach_vm_deallocate(g_kext_map, *start, size);
}

#endif //(__x86_64__) || defined(__i386__)

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::validateKCFileSetUUID(
	OSDictionary         *infoDict,
	kc_kind_t            type)
{
	OSReturn ret           = kOSReturnSuccess;

	if (!kernelcache_uuid_valid) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "validateKCFileSetUUID Boot KC UUID was not set at boot.");
		ret = kOSKextReturnInvalidArgument;
		goto finish;
	}
	ret = OSKext::validateKCUUIDfromPrelinkInfo(&kernelcache_uuid, type, infoDict, kPrelinkInfoBootKCIDKey);
	if (ret != 0) {
		goto finish;
	}

#if defined(__x86_64__) || defined(__i386__)
	/* Check if the Aux KC is prelinked to correct Pageable KC */
	if (type == KCKindAuxiliary) {
		if (!pageablekc_uuid_valid) {
			OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
			    "validateKCFileSetUUID Pageable KC UUID was not set while loading Pageable KC.");
			ret = kOSKextReturnInvalidArgument;
			goto finish;
		}
		ret = OSKext::validateKCUUIDfromPrelinkInfo(&pageablekc_uuid, type, infoDict, kPrelinkInfoPageableKCIDKey);
		if (ret != 0) {
			goto finish;
		}
	}
#endif //(__x86_64__) || defined(__i386__)

	printf("KextLog: Collection UUID matches with loaded KCs.\n");
finish:
	return ret;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::validateKCUUIDfromPrelinkInfo(
	uuid_t               *loaded_kcuuid,
	kc_kind_t             type,
	OSDictionary         *infoDict,
	const char           *uuid_key)
{
	/* extract the UUID from the dictionary */
	OSData *prelinkinfoKCUUID = OSDynamicCast(OSData, infoDict->getObject(uuid_key));
	if (!prelinkinfoKCUUID) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "validateKCUUID Info plist does not contain %s KC UUID key.", uuid_key);
		return kOSKextReturnInvalidArgument;
	}

	if (prelinkinfoKCUUID->getLength() != sizeof(uuid_t)) {
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "validateKCUUID %s KC UUID has wrong length: %d.", uuid_key, prelinkinfoKCUUID->getLength());
		return kOSKextReturnInvalidArgument;
	}

	if (memcmp((void *)loaded_kcuuid, (const void *)prelinkinfoKCUUID->getBytesNoCopy(),
	    prelinkinfoKCUUID->getLength())) {
		OSData       *info_dict_uuid;
		uuid_string_t info_dict_uuid_str = {};
		uuid_string_t expected_uuid_str = {};
		uuid_string_t given_uuid_str = {};
		uuid_t        given_uuid;

		/* extract the KC UUID from the dictionary */
		info_dict_uuid = OSDynamicCast(OSData, infoDict->getObject(kPrelinkInfoKCIDKey));
		if (info_dict_uuid && info_dict_uuid->getLength() == sizeof(uuid_t)) {
			uuid_t tmp_uuid;
			memcpy(tmp_uuid, (const void *)info_dict_uuid->getBytesNoCopy(), sizeof(tmp_uuid));
			uuid_unparse(tmp_uuid, info_dict_uuid_str);
		}

		uuid_unparse(*loaded_kcuuid, expected_uuid_str);
		memcpy(given_uuid, (const void *)prelinkinfoKCUUID->getBytesNoCopy(), sizeof(given_uuid));
		uuid_unparse(given_uuid, given_uuid_str);

		printf("KextLog: ERROR: UUID from key:%s %s != expected %s (KC UUID: %s)\n", uuid_key,
		    given_uuid_str, expected_uuid_str, info_dict_uuid_str);
		OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
		    "KextLog: ERROR: UUID from key:%s %s != expected %s (KC UUID: %s)\n", uuid_key,
		    given_uuid_str, expected_uuid_str, info_dict_uuid_str);
		if (type == KCKindPageable && sPanicOnKCMismatch) {
			panic("System KC UUID %s linked against %s, but %s is loaded",
			    info_dict_uuid_str, given_uuid_str, expected_uuid_str);
		}
		return kOSKextReturnInvalidArgument;
	}

	return 0;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::dispatchResource(OSDictionary * requestDict)
{
	OSReturn                        result          = kOSReturnError;
	OSSharedPtr<OSDictionary>       callbackRecord;
	OSNumber                      * requestTag      = NULL;        // do not release
	OSNumber                      * requestResult   = NULL;        // do not release
	OSData                        * dataObj         = NULL;        // do not release
	uint32_t                        dataLength      = 0;
	const void                    * dataPtr         = NULL;        // do not free
	OSValueObject<OSKextRequestResourceCallback> * callbackWrapper = nullptr; // do not release
	OSKextRequestResourceCallback   callback        = NULL;
	OSValueObject<void *>         * contextWrapper  = nullptr;     // do not release
	void                          * context         = NULL;        // do not free
	OSSharedPtr<OSKext>             callbackKext;

	/* Get the args from the request. Right now we need the tag
	 * to look up the callback record, and the result for invoking the callback.
	 */
	requestTag = OSDynamicCast(OSNumber, _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentRequestTagKey));
	requestResult = OSDynamicCast(OSNumber, _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentResultKey));
	if (!requestTag || !requestResult) {
		result = kOSKextReturnInvalidArgument;
		goto finish;
	}

	/* Look for a callback record matching this request's tag.
	 */
	result = dequeueCallbackForRequestTag(requestTag, callbackRecord);
	if (result != kOSReturnSuccess) {
		goto finish;
	}

	/*****
	 * Get the context pointer of the callback record (if there is one).
	 */
	contextWrapper = OSDynamicCast(OSValueObject<void *>, _OSKextGetRequestArgument(
		    callbackRecord.get(), kKextRequestArgumentContextKey));
	context = _OSKextExtractPointer(contextWrapper);
	if (contextWrapper && !context) {
		goto finish;
	}

	callbackWrapper = OSDynamicCast(OSValueObject<OSKextRequestResourceCallback>,
	    _OSKextGetRequestArgument(callbackRecord.get(),
	    kKextRequestArgumentCallbackKey));
	callback = _OSKextExtractCallbackPointer(callbackWrapper);
	if (!callback) {
		goto finish;
	}

	/* Check for a data obj. We might not have one and that's ok, that means
	 * we didn't find the requested resource, and we still have to tell the
	 * caller that via the callback.
	 */
	dataObj = OSDynamicCast(OSData, _OSKextGetRequestArgument(requestDict,
	    kKextRequestArgumentValueKey));
	if (dataObj) {
		dataPtr = dataObj->getBytesNoCopy();
		dataLength = dataObj->getLength();
	}

	callbackKext = OSKext::lookupKextWithAddress((vm_address_t)callback);
	if (!callbackKext) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "Can't invoke callback for resource request; ");
		goto finish;
	}
	if (!callbackKext->flags.starting && !callbackKext->flags.started) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogIPCFlag,
		    "Can't invoke kext resource callback; ");
		goto finish;
	}

	(void)callback(requestTag->unsigned32BitValue(),
	    (OSReturn)requestResult->unsigned32BitValue(),
	    dataPtr, dataLength, context);

	result = kOSReturnSuccess;

finish:
	return result;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::setMissingAuxKCBundles(OSDictionary * requestDict)
{
	OSSharedPtr<OSDictionary> missingIDs;
	OSArray *bundleIDList     = NULL; // do not release

	bundleIDList = OSDynamicCast(OSArray, _OSKextGetRequestArgument(
		    requestDict, kKextRequestArgumentMissingBundleIDs));
	if (!bundleIDList) {
		return kOSKextReturnInvalidArgument;
	}

	missingIDs = OSDictionary::withCapacity(bundleIDList->getCount());
	if (!missingIDs) {
		return kOSKextReturnNoMemory;
	}

	uint32_t count, i;
	count = bundleIDList->getCount();
	for (i = 0; i < count; i++) {
		OSString *thisID = OSDynamicCast(OSString, bundleIDList->getObject(i));
		if (thisID) {
			missingIDs->setObject(thisID, kOSBooleanFalse);
		}
	}

	sNonLoadableKextsByID.reset(missingIDs.get(), OSRetain);

	return kOSReturnSuccess;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::setAuxKCBundleAvailable(OSString *kextIdentifier, OSDictionary *requestDict)
{
	bool loadable = true;
	if (!kextIdentifier) {
		return kOSKextReturnInvalidArgument;
	}

	if (requestDict) {
		OSBoolean *loadableArg;
		loadableArg = OSDynamicCast(OSBoolean, _OSKextGetRequestArgument(
			    requestDict, kKextRequestArgumentBundleAvailability));
		/* If we find the "Bundle Available" arg, and it's false, then
		 * mark the bundle ID as _not_ loadable
		 */
		if (loadableArg && !loadableArg->getValue()) {
			loadable = false;
		}
	}

	if (!sNonLoadableKextsByID) {
		sNonLoadableKextsByID = OSDictionary::withCapacity(1);
	}

	sNonLoadableKextsByID->setObject(kextIdentifier, OSBoolean::withBoolean(loadable));

	OSKextLog(/* kext */ NULL,
	    kOSKextLogBasicLevel | kOSKextLogIPCFlag,
	    "KextLog: AuxKC bundle %s marked as %s",
	    kextIdentifier->getCStringNoCopy(),
	    (loadable ? "loadable" : "NOT loadable"));

	return kOSReturnSuccess;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::invokeRequestCallback(
	OSDictionary * callbackRecord,
	OSReturn       callbackResult)
{
	OSString * predicate  = _OSKextGetRequestPredicate(callbackRecord);
	OSSharedPtr<OSNumber> resultNum;

	if (!predicate) {
		goto finish;
	}

	resultNum  = OSNumber::withNumber((long long unsigned int)callbackResult,
	    8 * sizeof(callbackResult));
	if (!resultNum) {
		goto finish;
	}

	/* Insert the result into the callback record and dispatch it as if it
	 * were the reply coming down from user space.
	 */
	_OSKextSetRequestArgument(callbackRecord, kKextRequestArgumentResultKey,
	    resultNum.get());

	if (predicate->isEqualTo(kKextRequestPredicateRequestResource)) {
		/* This removes the pending callback record.
		 */
		OSKext::dispatchResource(callbackRecord);
	}

finish:
	return;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
OSReturn
OSKext::cancelRequest(
	OSKextRequestTag    requestTag,
	void             ** contextOut)
{
	OSReturn       result         = kOSKextReturnNoMemory;
	OSSharedPtr<OSDictionary> callbackRecord;
	OSValueObject<void *> * contextWrapper = nullptr; // do not release

	IORecursiveLockLock(sKextLock);
	result = OSKext::dequeueCallbackForRequestTag(requestTag,
	    callbackRecord);
	IORecursiveLockUnlock(sKextLock);

	if (result == kOSReturnSuccess && contextOut) {
		contextWrapper = OSDynamicCast(OSValueObject<void *>,
		    _OSKextGetRequestArgument(callbackRecord.get(),
		    kKextRequestArgumentContextKey));
		*contextOut = _OSKextExtractPointer(contextWrapper);
	}

	return result;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
void
OSKext::invokeOrCancelRequestCallbacks(
	OSReturn callbackResult,
	bool     invokeFlag)
{
	unsigned int count, i;

	count = sRequestCallbackRecords->getCount();
	if (!count) {
		goto finish;
	}

	i = count - 1;
	do {
		OSDictionary * request = OSDynamicCast(OSDictionary,
		    sRequestCallbackRecords->getObject(i));

		if (!request) {
			continue;
		}
		auto * callbackWrapper = OSDynamicCast(OSValueObject<OSKextRequestResourceCallback>,
		    _OSKextGetRequestArgument(request,
		    kKextRequestArgumentCallbackKey));

		if (!callbackWrapper) {
			sRequestCallbackRecords->removeObject(i);
			continue;
		}

		vm_address_t callbackAddress = (vm_address_t)
		    ptrauth_strip(_OSKextExtractPointer(callbackWrapper), ptrauth_key_function_pointer);

		if ((kmod_info->address <= callbackAddress) &&
		    (callbackAddress < (kmod_info->address + kmod_info->size))) {
			if (invokeFlag) {
				/* This removes the callback record.
				 */
				invokeRequestCallback(request, callbackResult);
			} else {
				sRequestCallbackRecords->removeObject(i);
			}
		}
	} while (i--);

finish:
	return;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
uint32_t
OSKext::countRequestCallbacks(void)
{
	uint32_t     result = 0;
	unsigned int count, i;

	count = sRequestCallbackRecords->getCount();
	if (!count) {
		goto finish;
	}

	i = count - 1;
	do {
		OSDictionary * request = OSDynamicCast(OSDictionary,
		    sRequestCallbackRecords->getObject(i));

		if (!request) {
			continue;
		}
		auto * callbackWrapper = OSDynamicCast(OSValueObject<OSKextRequestResourceCallback>,
		    _OSKextGetRequestArgument(request,
		    kKextRequestArgumentCallbackKey));

		if (!callbackWrapper) {
			continue;
		}

		vm_address_t callbackAddress = (vm_address_t)
		    ptrauth_strip(_OSKextExtractPointer(callbackWrapper), ptrauth_key_function_pointer);

		if ((kmod_info->address <= callbackAddress) &&
		    (callbackAddress < (kmod_info->address + kmod_info->size))) {
			result++;
		}
	} while (i--);

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
static OSReturn
_OSKextCreateRequest(
	const char    * predicate,
	OSSharedPtr<OSDictionary> & requestR)
{
	OSReturn result = kOSKextReturnNoMemory;
	OSSharedPtr<OSDictionary> request;

	request = OSDictionary::withCapacity(2);
	if (!request) {
		goto finish;
	}
	result = _OSDictionarySetCStringValue(request.get(),
	    kKextRequestPredicateKey, predicate);
	if (result != kOSReturnSuccess) {
		goto finish;
	}
	result = kOSReturnSuccess;

finish:
	if (result == kOSReturnSuccess) {
		requestR = os::move(request);
	}

	return result;
}

/*********************************************************************
*********************************************************************/
static OSString *
_OSKextGetRequestPredicate(OSDictionary * requestDict)
{
	return OSDynamicCast(OSString,
	           requestDict->getObject(kKextRequestPredicateKey));
}

/*********************************************************************
*********************************************************************/
static OSObject *
_OSKextGetRequestArgument(
	OSDictionary * requestDict,
	const char   * argName)
{
	OSDictionary * args = OSDynamicCast(OSDictionary,
	    requestDict->getObject(kKextRequestArgumentsKey));
	if (args) {
		return args->getObject(argName);
	}
	return NULL;
}

/*********************************************************************
*********************************************************************/
static bool
_OSKextSetRequestArgument(
	OSDictionary    * requestDict,
	const char      * argName,
	OSMetaClassBase * value)
{
	OSDictionary * args = OSDynamicCast(OSDictionary,
	    requestDict->getObject(kKextRequestArgumentsKey));
	OSSharedPtr<OSDictionary> newArgs;
	if (!args) {
		newArgs = OSDictionary::withCapacity(2);
		args = newArgs.get();
		if (!args) {
			goto finish;
		}
		requestDict->setObject(kKextRequestArgumentsKey, args);
	}
	if (args) {
		return args->setObject(argName, value);
	}
finish:
	return false;
}

/*********************************************************************
*********************************************************************/
template <typename T>
static T *
_OSKextExtractPointer(OSValueObject<T *> * wrapper)
{
	if (!wrapper) {
		return nullptr;
	}
	return wrapper->getRef();
}

/*********************************************************************
*********************************************************************/
static OSKextRequestResourceCallback
_OSKextExtractCallbackPointer(OSValueObject<OSKextRequestResourceCallback> * wrapper)
{
	if (!wrapper) {
		return nullptr;
	}
	return wrapper->getRef();
}


/*********************************************************************
*********************************************************************/
static OSReturn
_OSDictionarySetCStringValue(
	OSDictionary * dict,
	const char   * cKey,
	const char   * cValue)
{
	OSReturn result = kOSKextReturnNoMemory;
	OSSharedPtr<const OSSymbol> key;
	OSSharedPtr<OSString> value;

	key = OSSymbol::withCString(cKey);
	value = OSString::withCString(cValue);
	if (!key || !value) {
		goto finish;
	}
	if (dict->setObject(key.get(), value.get())) {
		result = kOSReturnSuccess;
	}

finish:
	return result;
}

/*********************************************************************
*********************************************************************/
static bool
_OSArrayContainsCString(
	OSArray    * array,
	const char * cString)
{
	bool             result = false;
	OSSharedPtr<const OSSymbol> symbol;
	uint32_t         count, i;

	if (!array || !cString) {
		goto finish;
	}

	symbol = OSSymbol::withCStringNoCopy(cString);
	if (!symbol) {
		goto finish;
	}

	count = array->getCount();
	for (i = 0; i < count; i++) {
		OSObject * thisObject = array->getObject(i);
		if (symbol->isEqualTo(thisObject)) {
			result = true;
			goto finish;
		}
	}

finish:
	return result;
}

#if CONFIG_KXLD
/*********************************************************************
* We really only care about boot / system start up related kexts.
* We return true if we're less than REBUILD_MAX_TIME since start up,
* otherwise return false.
*********************************************************************/
bool
_OSKextInPrelinkRebuildWindow(void)
{
	static bool     outside_the_window = false;
	AbsoluteTime    my_abstime;
	UInt64          my_ns;
	SInt32          my_secs;

	if (outside_the_window) {
		return false;
	}
	clock_get_uptime(&my_abstime);
	absolutetime_to_nanoseconds(my_abstime, &my_ns);
	my_secs = (SInt32)(my_ns / NSEC_PER_SEC);
	if (my_secs > REBUILD_MAX_TIME) {
		outside_the_window = true;
		return false;
	}
	return true;
}
#endif /* CONFIG_KXLD */

/*********************************************************************
*********************************************************************/
bool
_OSKextInUnloadedPrelinkedKexts( const OSSymbol * theBundleID )
{
	int unLoadedCount, i;
	bool result = false;

	IORecursiveLockLock(sKextLock);

	if (sUnloadedPrelinkedKexts == NULL) {
		goto finish;
	}
	unLoadedCount = sUnloadedPrelinkedKexts->getCount();
	if (unLoadedCount == 0) {
		goto finish;
	}

	for (i = 0; i < unLoadedCount; i++) {
		const OSSymbol *    myBundleID;        // do not release

		myBundleID = OSDynamicCast(OSSymbol, sUnloadedPrelinkedKexts->getObject(i));
		if (!myBundleID) {
			continue;
		}
		if (theBundleID->isEqualTo(myBundleID->getCStringNoCopy())) {
			result = true;
			break;
		}
	}
finish:
	IORecursiveLockUnlock(sKextLock);
	return result;
}

#if PRAGMA_MARK
#pragma mark Personalities (IOKit Drivers)
#endif
/*********************************************************************
*********************************************************************/
/* static */
OSSharedPtr<OSArray>
OSKext::copyAllKextPersonalities(bool filterSafeBootFlag)
{
	OSSharedPtr<OSArray>              result;
	OSSharedPtr<OSCollectionIterator> kextIterator;
	OSSharedPtr<OSArray>              personalities;

	OSString             * kextID                = NULL;        // do not release
	OSKext               * theKext               = NULL;        // do not release

	IORecursiveLockLock(sKextLock);

	/* Let's conservatively guess that any given kext has around 3
	 * personalities for now.
	 */
	result = OSArray::withCapacity(sKextsByID->getCount() * 3);
	if (!result) {
		goto finish;
	}

	kextIterator = OSCollectionIterator::withCollection(sKextsByID.get());
	if (!kextIterator) {
		goto finish;
	}

	while ((kextID = OSDynamicCast(OSString, kextIterator->getNextObject()))) {
		theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextID));
		if (theKext->flags.requireExplicitLoad) {
			OSKextLog(theKext,
			    kOSKextLogDebugLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s requires an explicit kextload; "
			    "omitting its personalities.",
			    theKext->getIdentifierCString());
		} else if (!sSafeBoot || !filterSafeBootFlag || theKext->isLoadableInSafeBoot()) {
			personalities = theKext->copyPersonalitiesArray();
			if (!personalities) {
				continue;
			}
			result->merge(personalities.get());
		} else {
			// xxx - check for better place to put this log msg
			OSKextLog(theKext,
			    kOSKextLogWarningLevel |
			    kOSKextLogLoadFlag,
			    "Kext %s is not loadable during safe boot; "
			    "omitting its personalities.",
			    theKext->getIdentifierCString());
		}
	}

finish:
	IORecursiveLockUnlock(sKextLock);

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::sendAllKextPersonalitiesToCatalog(bool startMatching)
{
	int numPersonalities = 0;

	OSKextLog(/* kext */ NULL,
	    kOSKextLogStepLevel |
	    kOSKextLogLoadFlag,
	    "Sending all eligible registered kexts' personalities "
	    "to the IOCatalogue %s.",
	    startMatching ? "and starting matching" : "but not starting matching");

	OSSharedPtr<OSArray> personalities = OSKext::copyAllKextPersonalities(
		/* filterSafeBootFlag */ true);

	if (personalities) {
		gIOCatalogue->addDrivers(personalities.get(), startMatching);
		numPersonalities = personalities->getCount();
	}

	OSKextLog(/* kext */ NULL,
	    kOSKextLogStepLevel |
	    kOSKextLogLoadFlag,
	    "%d kext personalit%s sent to the IOCatalogue; %s.",
	    numPersonalities, numPersonalities > 0 ? "ies" : "y",
	    startMatching ? "matching started" : "matching not started");
	return;
}

/*********************************************************************
* Do not make a deep copy, just convert the IOKitPersonalities dict
* to an array for sending to the IOCatalogue.
*********************************************************************/
OSSharedPtr<OSArray>
OSKext::copyPersonalitiesArray(void)
{
	OSSharedPtr<OSArray>              result;
	OSDictionary         * personalities               = NULL;        // do not release
	OSSharedPtr<OSCollectionIterator> personalitiesIterator;

	OSString             * personalityName             = NULL;        // do not release
	OSString             * personalityBundleIdentifier = NULL;        // do not release

	personalities = OSDynamicCast(OSDictionary,
	    getPropertyForHostArch(kIOKitPersonalitiesKey));
	if (!personalities) {
		goto finish;
	}

	result = OSArray::withCapacity(personalities->getCount());
	if (!result) {
		goto finish;
	}

	personalitiesIterator =
	    OSCollectionIterator::withCollection(personalities);
	if (!personalitiesIterator) {
		goto finish;
	}
	while ((personalityName = OSDynamicCast(OSString,
	    personalitiesIterator->getNextObject()))) {
		OSDictionary * personality = OSDynamicCast(OSDictionary,
		    personalities->getObject(personalityName));

		if (personality) {
			/******
			 * If the personality doesn't have a CFBundleIdentifier, or if it
			 * differs from the kext's, insert the kext's ID so we can find it.
			 * The publisher ID is used to remove personalities from bundles
			 * correctly.
			 */
			personalityBundleIdentifier = OSDynamicCast(OSString,
			    personality->getObject(kCFBundleIdentifierKey));

			if (!personalityBundleIdentifier) {
				personality->setObject(kCFBundleIdentifierKey, bundleID.get());
			} else if (!personalityBundleIdentifier->isEqualTo(bundleID.get())) {
				personality->setObject(kIOPersonalityPublisherKey, bundleID.get());
			}
		}

		result->setObject(personality);
	}

finish:
	return result;
}

/*********************************************************************
*   Might want to change this to a bool return?
*********************************************************************/
OSReturn
OSKext::sendPersonalitiesToCatalog(
	bool      startMatching,
	OSArray * personalityNames)
{
	OSReturn       result              = kOSReturnSuccess;
	OSSharedPtr<OSArray> personalitiesToSend;
	OSDictionary * kextPersonalities   = NULL;        // do not release
	int            count, i;

	if (!sLoadEnabled) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext loading is disabled (attempt to start matching for kext %s).",
		    getIdentifierCString());
		result = kOSKextReturnDisabled;
		goto finish;
	}

	if (sSafeBoot && !isLoadableInSafeBoot()) {
		OSKextLog(this,
		    kOSKextLogErrorLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s is not loadable during safe boot; "
		    "not sending personalities to the IOCatalogue.",
		    getIdentifierCString());
		result = kOSKextReturnNotLoadable;
		goto finish;
	}

	if (!personalityNames || !personalityNames->getCount()) {
		personalitiesToSend = copyPersonalitiesArray();
	} else {
		kextPersonalities = OSDynamicCast(OSDictionary,
		    getPropertyForHostArch(kIOKitPersonalitiesKey));
		if (!kextPersonalities || !kextPersonalities->getCount()) {
			// not an error
			goto finish;
		}
		personalitiesToSend = OSArray::withCapacity(0);
		if (!personalitiesToSend) {
			result = kOSKextReturnNoMemory;
			goto finish;
		}
		count = personalityNames->getCount();
		for (i = 0; i < count; i++) {
			OSString * name = OSDynamicCast(OSString,
			    personalityNames->getObject(i));
			if (!name) {
				continue;
			}
			OSDictionary * personality = OSDynamicCast(OSDictionary,
			    kextPersonalities->getObject(name));
			if (personality) {
				personalitiesToSend->setObject(personality);
			}
		}
	}
	if (personalitiesToSend) {
		unsigned numPersonalities = personalitiesToSend->getCount();
		OSKextLog(this,
		    kOSKextLogStepLevel |
		    kOSKextLogLoadFlag,
		    "Kext %s sending %d personalit%s to the IOCatalogue%s.",
		    getIdentifierCString(),
		    numPersonalities,
		    numPersonalities > 1 ? "ies" : "y",
		    startMatching ? " and starting matching" : " but not starting matching");
		gIOCatalogue->addDrivers(personalitiesToSend.get(), startMatching);
	}
finish:
	return result;
}

/*********************************************************************
* xxx - We should allow removing the kext's declared personalities,
* xxx - even with other bundle identifiers.
*********************************************************************/
void
OSKext::removePersonalitiesFromCatalog(void)
{
	OSSharedPtr<OSDictionary> personality;

	personality = OSDictionary::withCapacity(1);
	if (!personality) {
		goto finish;
	}
	personality->setObject(kCFBundleIdentifierKey, getIdentifier());

	OSKextLog(this,
	    kOSKextLogStepLevel |
	    kOSKextLogLoadFlag,
	    "Kext %s removing all personalities naming it from the IOCatalogue.",
	    getIdentifierCString());

	/* Have the IOCatalog remove all personalities matching this kext's
	 * bundle ID and trigger matching anew.
	 */
	gIOCatalogue->removeDrivers(personality.get(), /* startMatching */ true);

finish:
	return;
}

void
OSKext::updatePersonalitiesInCatalog(OSArray *upgradedPersonalities)
{
	if (!upgradedPersonalities || upgradedPersonalities->getCount() == 0) {
		return;
	}

	OSSharedPtr<OSDictionary> personalityToRemove = OSDictionary::withCapacity(1);
	if (!personalityToRemove) {
		return;
	}

	/*
	 * Create a personality dictionary with just the bundleID.
	 * We will remove any personality that has a matching bundleID,
	 * irrespective of which other keys are present on the dictionary.
	 */
	personalityToRemove->setObject(kCFBundleIdentifierKey, getIdentifier());
	gIOCatalogue->exchangeDrivers(personalityToRemove.get(), upgradedPersonalities, true);
}

#if PRAGMA_MARK
#pragma mark Logging
#endif
/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
OSKextLogSpec
OSKext::setUserSpaceLogFilter(
	OSKextLogSpec   newUserLogFilter,
	bool            captureFlag)
{
	OSKextLogSpec result;
	bool          allocError = false;

	/* Do not call any function that takes sKextLoggingLock during
	 * this critical block. That means do logging after.
	 */
	IOLockLock(sKextLoggingLock);

	result = sUserSpaceKextLogFilter;
	sUserSpaceKextLogFilter = newUserLogFilter;

	if (newUserLogFilter && captureFlag &&
	    !sUserSpaceLogSpecArray && !sUserSpaceLogMessageArray) {
		// xxx - do some measurements for a good initial capacity?
		sUserSpaceLogSpecArray = OSArray::withCapacity(0);
		sUserSpaceLogMessageArray = OSArray::withCapacity(0);

		if (!sUserSpaceLogSpecArray || !sUserSpaceLogMessageArray) {
			allocError = true;
		}
	}

	IOLockUnlock(sKextLoggingLock);

	/* If the config flag itself is changing, log the state change
	 * going both ways, before setting up the user-space log arrays,
	 * so that this is only logged in the kernel.
	 */
	if (result != newUserLogFilter) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogDebugLevel |
		    kOSKextLogGeneralFlag,
		    "User-space log flags changed from 0x%x to 0x%x.",
		    result, newUserLogFilter);
	}
	if (allocError) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel |
		    kOSKextLogGeneralFlag,
		    "Failed to allocate user-space log message arrays.");
	}

	return result;
}

/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
OSSharedPtr<OSArray>
OSKext::clearUserSpaceLogFilter(void)
{
	OSSharedPtr<OSArray>       result;
	OSKextLogSpec   oldLogFilter;
	OSKextLogSpec   newLogFilter = kOSKextLogSilentFilter;

	/* Do not call any function that takes sKextLoggingLock during
	 * this critical block. That means do logging after.
	 */
	IOLockLock(sKextLoggingLock);

	result = OSArray::withCapacity(2);
	if (result) {
		result->setObject(sUserSpaceLogSpecArray.get());
		result->setObject(sUserSpaceLogMessageArray.get());
	}
	sUserSpaceLogSpecArray.reset();
	sUserSpaceLogMessageArray.reset();

	oldLogFilter = sUserSpaceKextLogFilter;
	sUserSpaceKextLogFilter = newLogFilter;

	IOLockUnlock(sKextLoggingLock);

	/* If the config flag itself is changing, log the state change
	 * going both ways, after tearing down the user-space log
	 * arrays, so this is only logged within the kernel.
	 */
	if (oldLogFilter != newLogFilter) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogDebugLevel |
		    kOSKextLogGeneralFlag,
		    "User-space log flags changed from 0x%x to 0x%x.",
		    oldLogFilter, newLogFilter);
	}

	return result;
}


/*********************************************************************
* Do not call any function that takes sKextLock here!
*********************************************************************/
/* static */
OSKextLogSpec
OSKext::getUserSpaceLogFilter(void)
{
	OSKextLogSpec result;

	IOLockLock(sKextLoggingLock);
	result = sUserSpaceKextLogFilter;
	IOLockUnlock(sKextLoggingLock);

	return result;
}

/*********************************************************************
* This function is called by OSMetaClass during kernel C++ setup.
* Be careful what you access here; assume only OSKext::initialize()
* has been called.
*
* Do not call any function that takes sKextLock here!
*********************************************************************/
#define VTRESET   "\033[0m"

#define VTBOLD    "\033[1m"
#define VTUNDER   "\033[4m"

#define VTRED     "\033[31m"
#define VTGREEN   "\033[32m"
#define VTYELLOW  "\033[33m"
#define VTBLUE    "\033[34m"
#define VTMAGENTA "\033[35m"
#define VTCYAN    "\033[36m"

inline const char *
colorForFlags(OSKextLogSpec flags)
{
	OSKextLogSpec logLevel = flags & kOSKextLogLevelMask;

	switch (logLevel) {
	case kOSKextLogErrorLevel:
		return VTRED VTBOLD;
	case kOSKextLogWarningLevel:
		return VTRED;
	case kOSKextLogBasicLevel:
		return VTYELLOW VTUNDER;
	case kOSKextLogProgressLevel:
		return VTYELLOW;
	case kOSKextLogStepLevel:
		return VTGREEN;
	case kOSKextLogDetailLevel:
		return VTCYAN;
	case kOSKextLogDebugLevel:
		return VTMAGENTA;
	default:
		return "";         // white
	}
}

inline bool
logSpecMatch(
	OSKextLogSpec msgLogSpec,
	OSKextLogSpec logFilter)
{
	OSKextLogSpec filterKextGlobal  = logFilter & kOSKextLogKextOrGlobalMask;
	OSKextLogSpec filterLevel       = logFilter & kOSKextLogLevelMask;
	OSKextLogSpec filterFlags       = logFilter & kOSKextLogFlagsMask;

	OSKextLogSpec msgKextGlobal    = msgLogSpec & kOSKextLogKextOrGlobalMask;
	OSKextLogSpec msgLevel         = msgLogSpec & kOSKextLogLevelMask;
	OSKextLogSpec msgFlags         = msgLogSpec & kOSKextLogFlagsMask;

	/* Explicit messages always get logged.
	 */
	if (msgLevel == kOSKextLogExplicitLevel) {
		return true;
	}

	/* Warnings and errors are logged regardless of the flags.
	 */
	if (msgLevel <= kOSKextLogBasicLevel && (msgLevel <= filterLevel)) {
		return true;
	}

	/* A verbose message that isn't for a logging-enabled kext and isn't global
	 * does *not* get logged.
	 */
	if (!msgKextGlobal && !filterKextGlobal) {
		return false;
	}

	/* Warnings and errors are logged regardless of the flags.
	 * All other messages must fit the flags and
	 * have a level at or below the filter.
	 *
	 */
	if ((msgFlags & filterFlags) && (msgLevel <= filterLevel)) {
		return true;
	}
	return false;
}

extern "C" {
void
OSKextLog(
	OSKext         * aKext,
	OSKextLogSpec    msgLogSpec,
	const char     * format, ...)
{
	va_list argList;

	va_start(argList, format);
	OSKextVLog(aKext, msgLogSpec, format, argList);
	va_end(argList);
}

void
OSKextVLog(
	OSKext         * aKext,
	OSKextLogSpec    msgLogSpec,
	const char     * format,
	va_list          srcArgList)
{
	bool             logForKernel       = false;
	bool             logForUser         = false;
	va_list          argList;
	char             stackBuffer[120];
	uint32_t         length            = 0;
	char           * allocBuffer       = NULL;        // must kfree
	OSSharedPtr<OSNumber> logSpecNum;
	OSSharedPtr<OSString> logString;
	char           * buffer            = stackBuffer;        // do not free

	IOLockLock(sKextLoggingLock);

	/* Set the kext/global bit in the message spec if we have no
	 * kext or if the kext requests logging.
	 */
	if (!aKext || aKext->flags.loggingEnabled) {
		msgLogSpec = msgLogSpec | kOSKextLogKextOrGlobalMask;
	}

	logForKernel = logSpecMatch(msgLogSpec, sKernelLogFilter);
	if (sUserSpaceLogSpecArray && sUserSpaceLogMessageArray) {
		logForUser = logSpecMatch(msgLogSpec, sUserSpaceKextLogFilter);
	}

	if (!(logForKernel || logForUser)) {
		goto finish;
	}

	/* No goto from here until past va_end()!
	 */
	va_copy(argList, srcArgList);
	length = vsnprintf(stackBuffer, sizeof(stackBuffer), format, argList);
	va_end(argList);

	if (length + 1 >= sizeof(stackBuffer)) {
		allocBuffer = (char *)kalloc_data_tag(length + 1,
		    Z_WAITOK, VM_KERN_MEMORY_OSKEXT);
		if (!allocBuffer) {
			goto finish;
		}

		/* No goto from here until past va_end()!
		 */
		va_copy(argList, srcArgList);
		vsnprintf(allocBuffer, length + 1, format, argList);
		va_end(argList);

		buffer = allocBuffer;
	}

	/* If user space wants the log message, queue it up.
	 */
	if (logForUser && sUserSpaceLogSpecArray && sUserSpaceLogMessageArray) {
		logSpecNum = OSNumber::withNumber(msgLogSpec, 8 * sizeof(msgLogSpec));
		logString = OSString::withCString(buffer);
		if (logSpecNum && logString) {
			sUserSpaceLogSpecArray->setObject(logSpecNum.get());
			sUserSpaceLogMessageArray->setObject(logString.get());
		}
	}

	/* Always log messages from the kernel according to the kernel's
	 * log flags.
	 */
	if (logForKernel) {
		/* If we are in console mode and have a custom log filter,
		 * colorize the log message.
		 */
		if (sBootArgLogFilterFound) {
			const char * color = "";         // do not free
			color = colorForFlags(msgLogSpec);
			printf("%s%s%s\n", colorForFlags(msgLogSpec),
			    buffer, color[0] ? VTRESET : "");
		} else {
			printf("%s\n", buffer);
		}
	}

finish:
	IOLockUnlock(sKextLoggingLock);

	if (allocBuffer) {
		kfree_data(allocBuffer, length + 1);
	}
	return;
}

#if KASLR_IOREG_DEBUG

#define IOLOG_INDENT( the_indention ) \
{ \
    int     i; \
    for ( i = 0; i < (the_indention); i++ ) { \
	IOLog(" "); \
    } \
}

extern vm_offset_t       vm_kernel_stext;
extern vm_offset_t       vm_kernel_etext;
extern mach_vm_offset_t kext_alloc_base;
extern mach_vm_offset_t kext_alloc_max;

bool ScanForAddrInObject(OSObject * theObject,
    int indent );

bool
ScanForAddrInObject(OSObject * theObject,
    int indent)
{
	const OSMetaClass *     myTypeID;
	OSSharedPtr<OSCollectionIterator>  myIter;
	OSSymbol *              myKey;
	OSObject *              myValue;
	bool                    myResult = false;

	if (theObject == NULL) {
		IOLog("%s: theObject is NULL \n",
		    __FUNCTION__);
		return myResult;
	}

	myTypeID = OSTypeIDInst(theObject);

	if (myTypeID == OSTypeID(OSDictionary)) {
		OSDictionary *      myDictionary;

		myDictionary = OSDynamicCast(OSDictionary, theObject);
		myIter = OSCollectionIterator::withCollection( myDictionary );
		if (myIter == NULL) {
			return myResult;
		}

		// !! reset the iterator
		myIter->reset();

		while ((myKey = OSDynamicCast(OSSymbol, myIter->getNextObject()))) {
			bool    myTempResult;

			myValue = myDictionary->getObject(myKey);
			myTempResult = ScanForAddrInObject(myValue, (indent + 4));
			if (myTempResult) {
				// if we ever get a true result return true
				myResult = true;
				IOLOG_INDENT(indent);
				IOLog("OSDictionary key \"%s\" \n", myKey->getCStringNoCopy());
			}
		}

		// !! release the iterator
		myIter.reset();
	} else if (myTypeID == OSTypeID(OSArray)) {
		OSArray *   myArray;

		myArray = OSDynamicCast(OSArray, theObject);
		myIter = OSCollectionIterator::withCollection(myArray);
		if (myIter == NULL) {
			return myResult;
		}
		// !! reset the iterator
		myIter->reset();

		while ((myValue = myIter->getNextObject())) {
			bool        myTempResult;
			myTempResult = ScanForAddrInObject(myValue, (indent + 4));
			if (myTempResult) {
				// if we ever get a true result return true
				myResult = true;
				IOLOG_INDENT(indent);
				IOLog("OSArray: \n");
			}
		}
		// !! release the iterator
		myIter.reset();
	} else if (myTypeID == OSTypeID(OSString) || myTypeID == OSTypeID(OSSymbol)) {
		// should we look for addresses in strings?
	} else if (myTypeID == OSTypeID(OSData)) {
		void * *        myPtrPtr;
		unsigned int    myLen;
		OSData *        myDataObj;

		myDataObj =    OSDynamicCast(OSData, theObject);
		myPtrPtr = (void * *) myDataObj->getBytesNoCopy();
		myLen = myDataObj->getLength();

		if (myPtrPtr && myLen && myLen > 7) {
			int     i;
			int     myPtrCount = (myLen / sizeof(void *));

			for (i = 0; i < myPtrCount; i++) {
				UInt64 numberValue = (UInt64) * (myPtrPtr);

				if (kext_alloc_max != 0 &&
				    numberValue >= kext_alloc_base &&
				    numberValue < kext_alloc_max) {
					OSSharedPtr<OSKext> myKext;
					// IOLog("found OSData %p in kext map %p to %p  \n",
					//       *(myPtrPtr),
					//       (void *) kext_alloc_base,
					//       (void *) kext_alloc_max);

					myKext = OSKext::lookupKextWithAddress((vm_address_t) *(myPtrPtr));
					if (myKext) {
						IOLog("found addr %p from an OSData obj within kext \"%s\"  \n",
						    *(myPtrPtr),
						    myKext->getIdentifierCString());
					}
					myResult = true;
				}
				if (vm_kernel_etext != 0 &&
				    numberValue >= vm_kernel_stext &&
				    numberValue < vm_kernel_etext) {
					IOLog("found addr %p from an OSData obj within kernel text segment %p to %p  \n",
					    *(myPtrPtr),
					    (void *) vm_kernel_stext,
					    (void *) vm_kernel_etext);
					myResult = true;
				}
				myPtrPtr++;
			}
		}
	} else if (myTypeID == OSTypeID(OSBoolean)) {
		// do nothing here...
	} else if (myTypeID == OSTypeID(OSNumber)) {
		OSNumber * number = OSDynamicCast(OSNumber, theObject);

		UInt64 numberValue = number->unsigned64BitValue();

		if (kext_alloc_max != 0 &&
		    numberValue >= kext_alloc_base &&
		    numberValue < kext_alloc_max) {
			OSSharedPtr<OSKext> myKext;
			IOLog("found OSNumber in kext map %p to %p  \n",
			    (void *) kext_alloc_base,
			    (void *) kext_alloc_max);
			IOLog("OSNumber 0x%08llx (%llu) \n", numberValue, numberValue);

			myKext = OSKext::lookupKextWithAddress((vm_address_t) numberValue );
			if (myKext) {
				IOLog("found in kext \"%s\"  \n",
				    myKext->getIdentifierCString());
			}

			myResult = true;
		}
		if (vm_kernel_etext != 0 &&
		    numberValue >= vm_kernel_stext &&
		    numberValue < vm_kernel_etext) {
			IOLog("found OSNumber in kernel text segment %p to %p  \n",
			    (void *) vm_kernel_stext,
			    (void *) vm_kernel_etext);
			IOLog("OSNumber 0x%08llx (%llu) \n", numberValue, numberValue);
			myResult = true;
		}
	}
#if 0
	else {
		const OSMetaClass* myMetaClass = NULL;

		myMetaClass = theObject->getMetaClass();
		if (myMetaClass) {
			IOLog("class %s \n", myMetaClass->getClassName());
		} else {
			IOLog("Unknown object \n" );
		}
	}
#endif

	return myResult;
}
#endif // KASLR_KEXT_DEBUG
};         /* extern "C" */

#if PRAGMA_MARK
#pragma mark Backtrace Dump & kmod_get_info() support
#endif
/*********************************************************************
* This function must be safe to call in panic context.
*********************************************************************/
/* static */
void
OSKext::printKextsInBacktrace(
	vm_offset_t  * addr __unused,
	unsigned int   cnt __unused,
	int         (* printf_func)(const char *fmt, ...) __unused,
	uint32_t       flags __unused)
{
	addr64_t    summary_page = 0;
	addr64_t    last_summary_page = 0;

	if (kPrintKextsLock & flags) {
		if (!sKextSummariesLock) {
			return;
		}
		IOLockLock(sKextSummariesLock);
	}

	if (!gLoadedKextSummaries) {
		(*printf_func)("         can't perform kext scan: no kext summary");
		goto finish;
	}

	summary_page = trunc_page((addr64_t)(uintptr_t)gLoadedKextSummaries);
	last_summary_page = round_page(summary_page + sLoadedKextSummariesAllocSize);
	for (; summary_page < last_summary_page; summary_page += PAGE_SIZE) {
		if (pmap_find_phys(kernel_pmap, summary_page) == 0) {
			(*printf_func)("         can't perform kext scan: "
			    "missing kext summary page %p", summary_page);
			goto finish;
		}
	}

	foreachKextInBacktrace(addr, cnt, 0, ^(OSKextLoadedKextSummary *summary, uint32_t index) {
		if (index == 0 && !(kPrintKextsTerse & flags)) {
		        (*printf_func)("      Kernel Extensions in backtrace:\n");
		}

		printSummary(summary, printf_func, flags);
	});

finish:
	if (kPrintKextsLock & flags) {
		IOLockUnlock(sKextSummariesLock);
	}

	return;
}

void
OSKext::foreachKextInBacktrace(
	vm_offset_t   * addr,
	uint32_t        cnt,
	uint32_t        flags,
	void         (^ handler)(OSKextLoadedKextSummary *summary, uint32_t index))
{
	uint32_t n = 0;

	if (kPrintKextsLock & flags) {
		if (!sKextSummariesLock) {
			return;
		}
		IOLockLock(sKextSummariesLock);
	}

	for (uint32_t i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
		OSKextLoadedKextSummary * summary;

		summary = gLoadedKextSummaries->summaries + i;
		if (!summary->address) {
			continue;
		}

		if (!summaryIsInBacktrace(summary, addr, cnt)) {
			continue;
		}

		handler(summary, n++);
	}

	if (kPrintKextsLock & flags) {
		IOLockUnlock(sKextSummariesLock);
	}
}

/*********************************************************************
* This function must be safe to call in panic context.
*********************************************************************/
/* static */
boolean_t
OSKext::summaryIsInBacktrace(
	OSKextLoadedKextSummary   * summary,
	vm_offset_t               * addr,
	unsigned int                cnt)
{
	u_int i = 0;

	for (i = 0; i < cnt; i++) {
		vm_offset_t kscan_addr = addr[i];
#if  __has_feature(ptrauth_calls)
		kscan_addr = (vm_offset_t)VM_KERNEL_STRIP_PTR(kscan_addr);
#endif /*  __has_feature(ptrauth_calls) */
		if ((kscan_addr >= summary->text_exec_address) &&
		    (kscan_addr < (summary->text_exec_address + summary->text_exec_size))) {
			return TRUE;
		}
	}

	return FALSE;
}

/*
 * Get the kext summary object for the kext where 'addr' lies. Must be called with
 * sKextSummariesLock held.
 */
OSKextLoadedKextSummary *
OSKext::summaryForAddress(uintptr_t addr)
{
#if  __has_feature(ptrauth_calls)
	addr = (uintptr_t)VM_KERNEL_STRIP_PTR(addr);
#endif /*  __has_feature(ptrauth_calls) */
	for (unsigned i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
		OSKextLoadedKextSummary *summary = &gLoadedKextSummaries->summaries[i];
		if (!summary->address) {
			continue;
		}

#if VM_MAPPED_KEXTS
		/* On our platforms that use VM_MAPPED_KEXTS, we currently do not
		 * support split kexts, but we also may unmap the kexts, which can
		 * race with the above codepath (see OSKext::unload).  As such,
		 * use a simple range lookup if we are using VM_MAPPED_KEXTS.
		 */
		if ((addr >= summary->address) && (addr < (summary->address + summary->size))) {
			return summary;
		}
#else
		kernel_mach_header_t *mh = (kernel_mach_header_t *)summary->address;
		kernel_segment_command_t *seg;

		for (seg = firstsegfromheader(mh); seg != NULL; seg = nextsegfromheader(mh, seg)) {
			if ((addr >= seg->vmaddr) && (addr < (seg->vmaddr + seg->vmsize))) {
				return summary;
			}
		}
#endif
	}

	/* addr did not map to any kext */
	return NULL;
}

/* static */
void *
OSKext::kextForAddress(const void *address)
{
	OSKextActiveAccount * active;
	OSKext              * kext = NULL;
	uint32_t              baseIdx;
	uint32_t              lim;
	uintptr_t             addr = (uintptr_t) address;
	size_t                i;

	if (!addr) {
		return NULL;
	}
#if  __has_feature(ptrauth_calls)
	addr = (uintptr_t)VM_KERNEL_STRIP_PTR(addr);
#endif /*  __has_feature(ptrauth_calls) */

	if (sKextAccountsCount) {
		lck_ticket_lock(sKextAccountsLock, sKextAccountsLockGrp);
		// bsearch sKextAccounts list
		for (baseIdx = 0, lim = sKextAccountsCount; lim; lim >>= 1) {
			active = &sKextAccounts[baseIdx + (lim >> 1)];
			if ((addr >= active->address) && (addr < active->address_end)) {
				if (active->account &&
				    (kext = active->account->kext) &&
				    kext->kmod_info) {
					lck_ticket_unlock(sKextAccountsLock);
					return (void *)kext->kmod_info->address;
				}
				break;
			} else if (addr > active->address) {
				// move right
				baseIdx += (lim >> 1) + 1;
				lim--;
			}
			// else move left
		}
		lck_ticket_unlock(sKextAccountsLock);
	}
	if (kernel_text_contains(addr)) {
		return (void *)&_mh_execute_header;
	}
	if (gLoadedKextSummaries) {
		IOLockLock(sKextSummariesLock);
		for (i = 0; i < gLoadedKextSummaries->numSummaries; i++) {
			OSKextLoadedKextSummary *summary = gLoadedKextSummaries->summaries + i;
			if (addr >= summary->address && addr < summary->address + summary->size) {
				void *kextAddress = (void *)summary->address;
				IOLockUnlock(sKextSummariesLock);
				return kextAddress;
			}
		}
		IOLockUnlock(sKextSummariesLock);
	}

	return NULL;
}

/* static */
kern_return_t
OSKext::summaryForAddressExt(
	const void              * address,
	OSKextLoadedKextSummary * summary)
{
	kern_return_t                   result = KERN_FAILURE;
	const OSKextLoadedKextSummary * foundSummary = NULL;

	/*
	 * This needs to be safe to call even before the lock has been initialized
	 * in OSKext::initialize(), as we might get here from the ksancov runtime
	 * when instrumenting XNU itself with sanitizer coverage.
	 */
	if (!sKextSummariesLock) {
		return result;
	}

	IOLockLock(sKextSummariesLock);
	if (gLoadedKextSummaries) {
		foundSummary = summaryForAddress((uintptr_t)address);
		if (foundSummary) {
			memcpy(summary, foundSummary, sizeof(*summary));
			result = KERN_SUCCESS;
		} else {
			result = KERN_NOT_FOUND;
		}
	}
	IOLockUnlock(sKextSummariesLock);

	return result;
}

/*
 * Find a OSKextLoadedKextSummary given the ID from a kmod_info_t *
 * Safe to call in panic context.
 */
static OSKextLoadedKextSummary *
findSummary(uint32_t tagID)
{
	OSKextLoadedKextSummary * summary;
	for (size_t i = 0; i < gLoadedKextSummaries->numSummaries; ++i) {
		summary = gLoadedKextSummaries->summaries + i;
		if (summary->loadTag == tagID) {
			return summary;
		}
	}
	return NULL;
}

/*********************************************************************
* This function must be safe to call in panic context.
*********************************************************************/
void
OSKext::printSummary(
	OSKextLoadedKextSummary * summary,
	int                    (* printf_func)(const char *fmt, ...),
	uint32_t                  flags)
{
	kmod_reference_t * kmod_ref = NULL;
	uuid_string_t uuid;
	char version[kOSKextVersionMaxLength];
	uint64_t tmpAddr;
	uint64_t tmpSize;
	OSKextLoadedKextSummary *dependencySummary;

	if (!OSKextVersionGetString(summary->version, version, sizeof(version))) {
		strlcpy(version, "unknown version", sizeof(version));
	}
	(void) uuid_unparse(summary->uuid, uuid);

#if defined(__arm__) || defined(__arm64__)
	tmpAddr = summary->text_exec_address;
	tmpSize = summary->text_exec_size;
#else
	tmpAddr = summary->address;
	tmpSize = summary->size;
#endif
	if (kPrintKextsUnslide & flags) {
		tmpAddr = ml_static_unslide(tmpAddr);
	}
	(*printf_func)("%s%s(%s)[%s]@0x%llx->0x%llx\n",
	    (kPrintKextsTerse & flags) ? "" : "         ",
	    summary->name, version, uuid,
	    tmpAddr, tmpAddr + tmpSize - 1);

	if (kPrintKextsTerse & flags) {
		return;
	}

	/* print dependency info */
	for (kmod_ref = (kmod_reference_t *) summary->reference_list;
	    kmod_ref;
	    kmod_ref = kmod_ref->next) {
		kmod_info_t * rinfo;

		if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)kmod_ref)) == 0) {
			(*printf_func)("            kmod dependency scan stopped "
			    "due to missing dependency page: %p\n",
			    (kPrintKextsUnslide & flags) ? (void *)ml_static_unslide((vm_offset_t)kmod_ref) : kmod_ref);
			break;
		}
		rinfo = kmod_ref->info;

		if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)rinfo)) == 0) {
			(*printf_func)("            kmod dependency scan stopped "
			    "due to missing kmod page: %p\n",
			    (kPrintKextsUnslide & flags) ? (void *)ml_static_unslide((vm_offset_t)rinfo) : rinfo);
			break;
		}

		if (!rinfo->address) {
			continue;         // skip fake entries for built-ins
		}

		dependencySummary = findSummary(rinfo->id);
		uuid[0] = 0x00;
		tmpAddr = rinfo->address;
		tmpSize = rinfo->size;
		if (dependencySummary) {
			(void) uuid_unparse(dependencySummary->uuid, uuid);
#if defined(__arm__) || defined(__arm64__)
			tmpAddr = dependencySummary->text_exec_address;
			tmpSize = dependencySummary->text_exec_size;
#endif
		}

		if (kPrintKextsUnslide & flags) {
			tmpAddr = ml_static_unslide(tmpAddr);
		}
		(*printf_func)("            dependency: %s(%s)[%s]@%p->%p\n",
		    rinfo->name, rinfo->version, uuid, tmpAddr, tmpAddr + tmpSize - 1);
	}
	return;
}


#if !defined(__arm__) && !defined(__arm64__)
/*******************************************************************************
* substitute() looks at an input string (a pointer within a larger buffer)
* for a match to a substring, and on match it writes the marker & substitution
* character to an output string, updating the scan (from) and
* output (to) indexes as appropriate.
*******************************************************************************/
static int substitute(
	const char * scan_string,
	char       * string_out,
	uint32_t   * to_index,
	uint32_t   * from_index,
	const char * substring,
	char         marker,
	char         substitution);

/* string_out must be at least KMOD_MAX_NAME bytes.
 */
static int
substitute(
	const char * scan_string,
	char       * string_out,
	uint32_t   * to_index,
	uint32_t   * from_index,
	const char * substring,
	char         marker,
	char         substitution)
{
	size_t substring_length = strnlen(substring, KMOD_MAX_NAME - 1);

	/* On a substring match, append the marker (if there is one) and then
	 * the substitution character, updating the output (to) index accordingly.
	 * Then update the input (from) length by the length of the substring
	 * that got replaced.
	 */
	if (!strncmp(scan_string, substring, substring_length)) {
		if (marker) {
			string_out[(*to_index)++] = marker;
		}
		string_out[(*to_index)++] = substitution;
		(*from_index) += substring_length;
		return 1;
	}
	return 0;
}

/*******************************************************************************
* compactIdentifier() takes a CFBundleIdentifier in a buffer of at least
* KMOD_MAX_NAME characters and performs various substitutions of common
* prefixes & substrings as defined by tables in kext_panic_report.h.
*******************************************************************************/
static void compactIdentifier(
	const char * identifier,
	char       * identifier_out,
	char      ** identifier_out_end);

static void
compactIdentifier(
	const char * identifier,
	char       * identifier_out,
	char      ** identifier_out_end)
{
	uint32_t       from_index, to_index;
	uint32_t       scan_from_index = 0;
	uint32_t       scan_to_index   = 0;
	subs_entry_t * subs_entry    = NULL;
	int            did_sub       = 0;

	from_index = to_index = 0;
	identifier_out[0] = '\0';

	/* Replace certain identifier prefixes with shorter @+character sequences.
	 * Check the return value of substitute() so we only replace the prefix.
	 */
	for (subs_entry = &kext_identifier_prefix_subs[0];
	    subs_entry->substring && !did_sub;
	    subs_entry++) {
		did_sub = substitute(identifier, identifier_out,
		    &scan_to_index, &scan_from_index,
		    subs_entry->substring, /* marker */ '\0', subs_entry->substitute);
	}
	did_sub = 0;

	/* Now scan through the identifier looking for the common substrings
	 * and replacing them with shorter !+character sequences via substitute().
	 */
	for (/* see above */;
	    scan_from_index < KMOD_MAX_NAME - 1 && identifier[scan_from_index];
	    /* see loop */) {
		const char   * scan_string = &identifier[scan_from_index];

		did_sub = 0;

		if (scan_from_index) {
			for (subs_entry = &kext_identifier_substring_subs[0];
			    subs_entry->substring && !did_sub;
			    subs_entry++) {
				did_sub = substitute(scan_string, identifier_out,
				    &scan_to_index, &scan_from_index,
				    subs_entry->substring, '!', subs_entry->substitute);
			}
		}

		/* If we didn't substitute, copy the input character to the output.
		 */
		if (!did_sub) {
			identifier_out[scan_to_index++] = identifier[scan_from_index++];
		}
	}

	identifier_out[scan_to_index] = '\0';
	if (identifier_out_end) {
		*identifier_out_end = &identifier_out[scan_to_index];
	}

	return;
}
#endif /* !defined(__arm__) && !defined(__arm64__) */

/*******************************************************************************
* assemble_identifier_and_version() adds to a string buffer a compacted
* bundle identifier followed by a version string.
*******************************************************************************/

/* identPlusVers must be at least 2*KMOD_MAX_NAME in length.
 */
static size_t assemble_identifier_and_version(
	kmod_info_t * kmod_info,
	char        * identPlusVers,
	size_t        bufSize);

static size_t
assemble_identifier_and_version(
	kmod_info_t * kmod_info,
	char        * identPlusVers,
	size_t        bufSize)
{
	size_t result = 0;

#if defined(__arm__) || defined(__arm64__)
	result = strlcpy(identPlusVers, kmod_info->name, KMOD_MAX_NAME);
#else
	compactIdentifier(kmod_info->name, identPlusVers, NULL);
	result = strnlen(identPlusVers, KMOD_MAX_NAME - 1);
#endif
	identPlusVers[result++] = '\t';         // increment for real char
	identPlusVers[result] = '\0';         // don't increment for nul char
	result = strlcat(identPlusVers, kmod_info->version, bufSize);
	if (result >= bufSize) {
		identPlusVers[bufSize - 1] = '\0';
		result = bufSize - 1;
	}

	return result;
}

/*******************************************************************************
* Assumes sKextLock is held.
*******************************************************************************/
/* static */
int
OSKext::saveLoadedKextPanicListTyped(
	const char * prefix,
	int          invertFlag,
	int          libsFlag,
	char       * paniclist,
	uint32_t     list_size)
{
	int             result = -1;
	unsigned int    count, i;

	count = sLoadedKexts->getCount();
	if (!count) {
		goto finish;
	}

	i = count - 1;
	do {
		OSObject    * rawKext = sLoadedKexts->getObject(i);
		OSKext      * theKext = OSDynamicCast(OSKext, rawKext);
		int           match;
		size_t        identPlusVersLength;
		size_t        tempLen;
		char          identPlusVers[2 * KMOD_MAX_NAME];

		if (!rawKext) {
			printf("OSKext::saveLoadedKextPanicListTyped - "
			    "NULL kext in loaded kext list; continuing\n");
			continue;
		}

		if (!theKext) {
			printf("OSKext::saveLoadedKextPanicListTyped - "
			    "Kext type cast failed in loaded kext list; continuing\n");
			continue;
		}

		/* Skip all built-in kexts.
		 */
		if (theKext->isKernelComponent()) {
			continue;
		}

		kmod_info_t * kmod_info = theKext->kmod_info;

		/* Filter for kmod name (bundle identifier).
		 */
		match = !strncmp(kmod_info->name, prefix, strnlen(prefix, KMOD_MAX_NAME));
		if ((match && invertFlag) || (!match && !invertFlag)) {
			continue;
		}

		/* Filter for libraries (kexts that have a compatible version).
		 */
		if ((libsFlag == 0 && theKext->getCompatibleVersion() > 1) ||
		    (libsFlag == 1 && theKext->getCompatibleVersion() < 1)) {
			continue;
		}

		if (!kmod_info ||
		    !pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)kmod_info))) {
			printf("kext scan stopped due to missing kmod_info page: %p\n",
			    kmod_info);
			goto finish;
		}

		identPlusVersLength = assemble_identifier_and_version(kmod_info,
		    identPlusVers,
		    sizeof(identPlusVers));
		if (!identPlusVersLength) {
			printf("error saving loaded kext info\n");
			goto finish;
		}

		/* make sure everything fits and we null terminate.
		 */
		tempLen = strlcat(paniclist, identPlusVers, list_size);
		if (tempLen >= list_size) {
			// panic list is full, keep it and null terminate
			paniclist[list_size - 1] = 0x00;
			result = 0;
			goto finish;
		}
		tempLen = strlcat(paniclist, "\n", list_size);
		if (tempLen >= list_size) {
			// panic list is full, keep it and null terminate
			paniclist[list_size - 1] = 0x00;
			result = 0;
			goto finish;
		}
	} while (i--);

	result = 0;
finish:

	return result;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::saveLoadedKextPanicList(void)
{
	char     * newlist        = NULL;
	uint32_t   newlist_size   = 0;

	newlist_size = KEXT_PANICLIST_SIZE;
	newlist = (char *)kalloc_data_tag(newlist_size,
	    Z_WAITOK, VM_KERN_MEMORY_OSKEXT);

	if (!newlist) {
		OSKextLog(/* kext */ NULL,
		    kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
		    "Couldn't allocate kext panic log buffer.");
		goto finish;
	}

	newlist[0] = '\0';

	// non-"com.apple." kexts
	if (OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 1,
	    /* libs? */ -1, newlist, newlist_size) != 0) {
		goto finish;
	}
	// "com.apple." nonlibrary kexts
	if (OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0,
	    /* libs? */ 0, newlist, newlist_size) != 0) {
		goto finish;
	}
	// "com.apple." library kexts
	if (OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0,
	    /* libs? */ 1, newlist, newlist_size) != 0) {
		goto finish;
	}

	if (loaded_kext_paniclist) {
		kfree_data(loaded_kext_paniclist, loaded_kext_paniclist_size);
	}
	loaded_kext_paniclist = newlist;
	newlist = NULL;
	loaded_kext_paniclist_size = newlist_size;

finish:
	if (newlist) {
		kfree_data(newlist, newlist_size);
	}
	return;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
void
OSKext::savePanicString(bool isLoading)
{
	u_long len;

	if (!kmod_info) {
		return;         // do not goto finish here b/c of lock
	}

	len = assemble_identifier_and_version( kmod_info,
	    (isLoading) ? last_loaded_str_buf : last_unloaded_str_buf,
	    (isLoading) ? sizeof(last_loaded_str_buf) : sizeof(last_unloaded_str_buf));
	if (!len) {
		printf("error saving unloaded kext info\n");
		goto finish;
	}

	if (isLoading) {
		last_loaded_strlen = len;
		last_loaded_address = (void *)kmod_info->address;
		last_loaded_size = kmod_info->size;
		clock_get_uptime(&last_loaded_timestamp);
	} else {
		last_unloaded_strlen = len;
		last_unloaded_address = (void *)kmod_info->address;
		last_unloaded_size = kmod_info->size;
		clock_get_uptime(&last_unloaded_timestamp);
	}

finish:
	return;
}

/*********************************************************************
*********************************************************************/
/* static */
void
OSKext::printKextPanicLists(int (*printf_func)(const char *fmt, ...))
{
	if (last_loaded_strlen) {
		printf_func("last started kext at %llu: %.*s (addr %p, size %lu)\n",
		    AbsoluteTime_to_scalar(&last_loaded_timestamp),
		    last_loaded_strlen, last_loaded_str_buf,
		    last_loaded_address, last_loaded_size);
	}

	if (last_unloaded_strlen) {
		printf_func("last stopped kext at %llu: %.*s (addr %p, size %lu)\n",
		    AbsoluteTime_to_scalar(&last_unloaded_timestamp),
		    last_unloaded_strlen, last_unloaded_str_buf,
		    last_unloaded_address, last_unloaded_size);
	}

	printf_func("loaded kexts:\n");
	if (loaded_kext_paniclist &&
	    pmap_find_phys(kernel_pmap, (addr64_t) (uintptr_t) loaded_kext_paniclist) &&
	    loaded_kext_paniclist[0]) {
		printf_func("%.*s",
		    strnlen(loaded_kext_paniclist, loaded_kext_paniclist_size),
		    loaded_kext_paniclist);
	} else {
		printf_func("(none)\n");
	}
	return;
}

/*********************************************************************
* Assumes sKextLock is held.
*********************************************************************/
/* static */
void
OSKext::updateLoadedKextSummaries(void)
{
	kern_return_t result = KERN_FAILURE;
	OSKextLoadedKextSummaryHeader *summaryHeader = NULL;
	OSKextLoadedKextSummaryHeader *summaryHeaderAlloc = NULL;
	OSKext *aKext;
	vm_map_offset_t start;
	size_t summarySize = 0;
	size_t size;
	u_int count;
	u_int maxKexts;
	u_int i, j;
	OSKextActiveAccount * accountingList;
	OSKextActiveAccount * prevAccountingList;
	uint32_t idx, accountingListAlloc, accountingListCount, prevAccountingListCount;

	prevAccountingList = NULL;
	prevAccountingListCount = 0;

#if DEVELOPMENT || DEBUG
	if (IORecursiveLockHaveLock(sKextLock) == false) {
		panic("sKextLock must be held");
	}
#endif

	IOLockLock(sKextSummariesLock);

	count = sLoadedKexts->getCount();
	for (i = 0, maxKexts = 0; i < count; ++i) {
		aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
		maxKexts += (aKext && (aKext->isExecutable() || aKext->isSpecialKernelBinary()));
	}

	if (!maxKexts) {
		goto finish;
	}
	if (maxKexts < kOSKextTypicalLoadCount) {
		maxKexts = kOSKextTypicalLoadCount;
	}

	/* Calculate the size needed for the new summary headers.
	 */

	size = sizeof(*gLoadedKextSummaries);
	size += maxKexts * sizeof(*gLoadedKextSummaries->summaries);
	size = round_page(size);

	if (gLoadedKextSummaries == NULL || sLoadedKextSummariesAllocSize < size) {
		if (gLoadedKextSummaries) {
			kmem_free(kernel_map, (vm_offset_t)gLoadedKextSummaries, sLoadedKextSummariesAllocSize);
			gLoadedKextSummaries = NULL;
			gLoadedKextSummariesTimestamp = mach_absolute_time();
			sLoadedKextSummariesAllocSize = 0;
		}
		result = kmem_alloc(kernel_map, (vm_offset_t *)&summaryHeaderAlloc, size,
		    KMA_NONE, VM_KERN_MEMORY_OSKEXT);
		if (result != KERN_SUCCESS) {
			goto finish;
		}
		summaryHeader = summaryHeaderAlloc;
		summarySize = size;
	} else {
		summaryHeader = gLoadedKextSummaries;
		summarySize = sLoadedKextSummariesAllocSize;

		start = (vm_map_offset_t) summaryHeader;
		result = mach_vm_protect(kernel_map,
		    start,
		    summarySize,
		    false,
		    VM_PROT_DEFAULT);
		if (result != KERN_SUCCESS) {
			goto finish;
		}
	}

	/* Populate the summary header.
	 */

	bzero(summaryHeader, summarySize);
	summaryHeader->version = kOSKextLoadedKextSummaryVersion;
	summaryHeader->entry_size = sizeof(OSKextLoadedKextSummary);

	/* Populate each kext summary.
	 */

	count = sLoadedKexts->getCount();
	accountingListAlloc = 0;
	for (i = 0, j = 0; i < count && j < maxKexts; ++i) {
		aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
		if (!aKext || (!aKext->isExecutable() && !aKext->isSpecialKernelBinary())) {
			continue;
		}

		aKext->updateLoadedKextSummary(&summaryHeader->summaries[j++]);
		summaryHeader->numSummaries++;
		accountingListAlloc++;
	}

	accountingList = IONew(typeof(accountingList[0]), accountingListAlloc);
	accountingListCount = 0;
	for (i = 0, j = 0; i < count && j < maxKexts; ++i) {
		aKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i));
		if (!aKext || (!aKext->isExecutable() && !aKext->isSpecialKernelBinary())) {
			continue;
		}

		OSKextActiveAccount activeAccount;
		aKext->updateActiveAccount(&activeAccount);
		// order by address
		for (idx = 0; idx < accountingListCount; idx++) {
			if (activeAccount.address < accountingList[idx].address) {
				break;
			}
		}
		bcopy(&accountingList[idx], &accountingList[idx + 1], (accountingListCount - idx) * sizeof(accountingList[0]));
		accountingList[idx] = activeAccount;
		accountingListCount++;
	}
	assert(accountingListCount == accountingListAlloc);
	/* Write protect the buffer and move it into place.
	 */

	start = (vm_map_offset_t) summaryHeader;

	result = mach_vm_protect(kernel_map, start, summarySize, false, VM_PROT_READ);
	if (result != KERN_SUCCESS) {
		goto finish;
	}

	gLoadedKextSummaries = summaryHeader;
	gLoadedKextSummariesTimestamp = mach_absolute_time();
	sLoadedKextSummariesAllocSize = summarySize;
	summaryHeaderAlloc = NULL;

	/* Call the magic breakpoint function through a static function pointer so
	 * the compiler can't optimize the function away.
	 */
	if (sLoadedKextSummariesUpdated) {
		(*sLoadedKextSummariesUpdated)();
	}

	lck_ticket_lock(sKextAccountsLock, sKextAccountsLockGrp);
	prevAccountingList      = sKextAccounts;
	prevAccountingListCount = sKextAccountsCount;
	sKextAccounts           = accountingList;
	sKextAccountsCount      = accountingListCount;
	lck_ticket_unlock(sKextAccountsLock);

finish:
	IOLockUnlock(sKextSummariesLock);

	/* If we had to allocate a new buffer but failed to generate the summaries,
	 * free that now.
	 */
	if (summaryHeaderAlloc) {
		kmem_free(kernel_map, (vm_offset_t)summaryHeaderAlloc, summarySize);
	}
	if (prevAccountingList) {
		IODelete(prevAccountingList, typeof(accountingList[0]), prevAccountingListCount);
	}

	return;
}

/*********************************************************************
*********************************************************************/
void
OSKext::updateLoadedKextSummary(OSKextLoadedKextSummary *summary)
{
	OSSharedPtr<OSData> uuid;

	strlcpy(summary->name, getIdentifierCString(),
	    sizeof(summary->name));

	uuid = copyUUID();
	if (uuid) {
		memcpy(summary->uuid, uuid->getBytesNoCopy(), sizeof(summary->uuid));
	}

	if (flags.builtin) {
//      this value will stop lldb from parsing the mach-o header
//      summary->address = UINT64_MAX;
//      summary->size = 0;
		summary->address = kmod_info->address;
		summary->size = kmod_info->size;
	} else {
		summary->address = kmod_info->address;
		summary->size = kmod_info->size;
	}
	summary->version = getVersion();
	summary->loadTag = kmod_info->id;
	summary->flags = 0;
	summary->reference_list = (uint64_t) kmod_info->reference_list;

	summary->text_exec_address = (uint64_t) getsegdatafromheader((kernel_mach_header_t *)summary->address, "__TEXT_EXEC", &summary->text_exec_size);
	if (summary->text_exec_address == 0) {
		// Fallback to __TEXT
		summary->text_exec_address = (uint64_t) getsegdatafromheader((kernel_mach_header_t *)summary->address, "__TEXT", &summary->text_exec_size);
	}

	/**
	 * If the addresses within the Mach-O are unslid, then manually slide any
	 * addresses coming from the Mach-O as higher layer software using these
	 * summaries expects a slid address here.
	 */
	if (flags.unslidMachO) {
		summary->text_exec_address = (uint64_t) ml_static_slide((vm_offset_t) summary->text_exec_address);
	}

	return;
}

/*********************************************************************
*********************************************************************/

void
OSKext::updateActiveAccount(OSKextActiveAccount *accountp)
{
	kernel_mach_header_t     *hdr = NULL;
	kernel_segment_command_t *seg = NULL;

	bzero(accountp, sizeof(*accountp));

	hdr = (kernel_mach_header_t *)kmod_info->address;
	if (getcommandfromheader(hdr, LC_SEGMENT_SPLIT_INFO) || isInFileset()) {
		/*
		 * If this kext supports split segments (or is in a new
		 * MH_FILESET kext collection), use the first
		 * executable segment as the range for instructions
		 * (and thus for backtracing.
		 */
		for (seg = firstsegfromheader(hdr); seg != NULL; seg = nextsegfromheader(hdr, seg)) {
			if (seg->initprot & VM_PROT_EXECUTE) {
				break;
			}
		}
	}
	if (seg) {
		accountp->address = seg->vmaddr;
		if (accountp->address) {
			accountp->address_end = seg->vmaddr + seg->vmsize;
		}
	} else {
		/* For non-split kexts and for kexts without executable
		 * segments, just use the kmod_info range (as the kext
		 * is either all in one range or should not show up in
		 * instruction backtraces).
		 */
		accountp->address = kmod_info->address;
		if (accountp->address) {
			accountp->address_end = kmod_info->address + kmod_info->size;
		}
	}

	accountp->account = this->account;
}

bool
OSKext::isDriverKit(void)
{
	OSString *bundleType;

	if (infoDict) {
		bundleType = OSDynamicCast(OSString, infoDict->getObject(kCFBundlePackageTypeKey));
		if (bundleType && bundleType->isEqualTo(kOSKextBundlePackageTypeDriverKit)) {
			return TRUE;
		}
	}
	return FALSE;
}

bool
OSKext::isInFileset(void)
{
	if (!kmod_info) {
		goto check_prelinked;
	}

	if (kmod_info->address && kernel_mach_header_is_in_fileset((kernel_mach_header_t *)kmod_info->address)) {
		return true;
	}

check_prelinked:
	if (isPrelinked()) {
		/*
		 * If we haven't setup kmod_info yet, but we know
		 * we're loading a prelinked kext in an MH_FILESET KC,
		 * then return true
		 */
		kc_format_t kc_format;
		if (PE_get_primary_kc_format(&kc_format) && kc_format == KCFormatFileset) {
			return true;
		}
	}
	return false;
}

OSSharedPtr<OSDextStatistics>
OSKext::copyDextStatistics(void)
{
	return dextStatistics;
}

bool
OSKextSavedMutableSegment::initWithSegment(kernel_segment_command_t *seg)
{
	kern_return_t result;
	if (!super::init()) {
		return false;
	}
	if (seg == nullptr) {
		return false;
	}
	result = kmem_alloc(kernel_map, (vm_offset_t *)&data, seg->vmsize,
	    KMA_PAGEABLE, VM_KERN_MEMORY_KEXT);
	if (result != KERN_SUCCESS) {
		return false;
	}
	memcpy((void *)data, (const void *)seg->vmaddr, seg->vmsize);
	savedSegment = seg;
	vmsize = seg->vmsize;
	vmaddr = seg->vmaddr;
	return true;
}

OSSharedPtr<OSKextSavedMutableSegment>
OSKextSavedMutableSegment::withSegment(kernel_segment_command_t *seg)
{
	OSSharedPtr<OSKextSavedMutableSegment> me = OSMakeShared<OSKextSavedMutableSegment>();
	if (me && !me->initWithSegment(seg)) {
		return nullptr;
	}
	return me;
}

void
OSKextSavedMutableSegment::free(void)
{
	if (data) {
		kmem_free(kernel_map, (vm_offset_t)data, vmsize);
	}
}

vm_offset_t
OSKextSavedMutableSegment::getVMAddr() const
{
	return vmaddr;
}

vm_offset_t
OSKextSavedMutableSegment::getVMSize() const
{
	return vmsize;
}

OSReturn
OSKextSavedMutableSegment::restoreContents(kernel_segment_command_t *seg)
{
	if (seg != savedSegment) {
		return kOSKextReturnInvalidArgument;
	}
	if (seg->vmaddr != vmaddr || seg->vmsize != vmsize) {
		return kOSKextReturnInvalidArgument;
	}
	memcpy((void *)seg->vmaddr, data, vmsize);
	return kOSReturnSuccess;
}

extern "C" kern_return_t
OSKextSetReceiptQueried(void)
{
	OSKextLog(/* kext */ NULL,
	    kOSKextLogStepLevel | kOSKextLogGeneralFlag,
	    "Setting kext receipt as queried");

	IOService::publishResource(kOSKextReceiptQueried, kOSBooleanTrue);
	return KERN_SUCCESS;
}

extern "C" const vm_allocation_site_t *
OSKextGetAllocationSiteForCaller(uintptr_t address)
{
	OSKextActiveAccount *  active;
	vm_allocation_site_t * site;
	vm_allocation_site_t * releasesite;

	uint32_t baseIdx;
	uint32_t lim;
#if  __has_feature(ptrauth_calls)
	address = (uintptr_t)VM_KERNEL_STRIP_PTR(address);
#endif /*  __has_feature(ptrauth_calls) */

	lck_ticket_lock(sKextAccountsLock, sKextAccountsLockGrp);
	site = releasesite = NULL;

	// bsearch sKextAccounts list
	for (baseIdx = 0, lim = sKextAccountsCount; lim; lim >>= 1) {
		active = &sKextAccounts[baseIdx + (lim >> 1)];
		if ((address >= active->address) && (address < active->address_end)) {
			site = &active->account->site;
			if (!site->tag) {
				vm_tag_alloc_locked(site, &releasesite);
			}
			break;
		} else if (address > active->address) {
			// move right
			baseIdx += (lim >> 1) + 1;
			lim--;
		}
		// else move left
	}
	lck_ticket_unlock(sKextAccountsLock);
	if (releasesite) {
		kern_allocation_name_release(releasesite);
	}

	return site;
}

#if DEVELOPMENT || DEBUG
extern "C" void
OSKextGetRefGrpForCaller(uintptr_t address, void (^cb)(struct os_refgrp *))
{
	OSKextActiveAccount *  active;

	uint32_t baseIdx;
	uint32_t lim;
#if  __has_feature(ptrauth_calls)
	address = (uintptr_t)VM_KERNEL_STRIP_PTR(address);
#endif /*  __has_feature(ptrauth_calls) */

	lck_ticket_lock(sKextAccountsLock, sKextAccountsLockGrp);

	// bsearch sKextAccounts list
	for (baseIdx = 0, lim = sKextAccountsCount; lim; lim >>= 1) {
		active = &sKextAccounts[baseIdx + (lim >> 1)];
		if ((address >= active->address) && (address < active->address_end)) {
			cb(&active->account->task_refgrp);
			break;
		} else if (address > active->address) {
			// move right
			baseIdx += (lim >> 1) + 1;
			lim--;
		}
		// else move left
	}
	lck_ticket_unlock(sKextAccountsLock);
}
#endif /* DEVELOPMENT || DEBUG */

extern "C" uint32_t
OSKextGetKmodIDForSite(const vm_allocation_site_t * site, char * name, vm_size_t namelen)
{
	OSKextAccount * account = (typeof(account))site;
	const char    * kname;

	if (name) {
		if (account->kext) {
			kname = account->kext->getIdentifierCString();
		} else {
			kname = "<>";
		}
		strlcpy(name, kname, namelen);
	}

	return account->loadTag;
}

extern "C" void
OSKextFreeSite(vm_allocation_site_t * site)
{
	OSKextAccount * freeAccount = (typeof(freeAccount))site;
	IOFreeType(freeAccount, OSKextAccount);
}

/*********************************************************************
*********************************************************************/

#if CONFIG_IMAGEBOOT
int
OSKextGetUUIDForName(const char *name, uuid_t uuid)
{
	OSSharedPtr<OSKext> kext = OSKext::lookupKextWithIdentifier(name);
	if (!kext) {
		return 1;
	}

	OSSharedPtr<OSData> uuid_data = kext->copyUUID();
	if (uuid_data) {
		memcpy(uuid, uuid_data->getBytesNoCopy(), sizeof(uuid_t));
		return 0;
	}

	return 1;
}
#endif



class OSDextCrash : public OSObject {
	OSDeclareDefaultStructors(OSDextCrash);
public:
	static OSPtr<OSDextCrash> withTimestamp(uint64_t timestamp);
	uint64_t getTimestamp();

private:
	virtual bool initWithTimestamp(uint64_t timestamp);
	uint64_t fTimestamp;
};

OSDefineMetaClassAndStructors(OSDextCrash, OSObject);

OSSharedPtr<OSDextCrash>
OSDextCrash::withTimestamp(uint64_t timestamp)
{
	OSSharedPtr<OSDextCrash> result = OSMakeShared<OSDextCrash>();
	if (!result->initWithTimestamp(timestamp)) {
		return NULL;
	}
	return result;
}

bool
OSDextCrash::initWithTimestamp(uint64_t timestamp)
{
	if (!OSObject::init()) {
		return false;
	}
	fTimestamp = timestamp;
	return true;
}

uint64_t
OSDextCrash::getTimestamp()
{
	return fTimestamp;
}

OSSharedPtr<OSDextStatistics>
OSDextStatistics::create()
{
	OSSharedPtr<OSDextStatistics> result = OSMakeShared<OSDextStatistics>();
	if (!result->init()) {
		return NULL;
	}
	return result;
}

bool
OSDextStatistics::init()
{
	if (!OSObject::init()) {
		return false;
	}

	lock = IOLockAlloc();
	crashes = OSArray::withCapacity(kMaxDextCrashesInOneDay);
	return true;
}

void
OSDextStatistics::free()
{
	if (lock) {
		IOLockFree(lock);
	}
	crashes.reset();
	OSObject::free();
}

OSDextCrashPolicy
OSDextStatistics::recordCrash()
{
	size_t i = 0;
	uint64_t timestamp = mach_continuous_time();
	uint64_t interval;
	nanoseconds_to_absolutetime(86400 * NSEC_PER_SEC /* 1 day */, &interval);
	uint64_t lastTimestamp = timestamp > interval ? timestamp - interval : 0;
	OSDextCrashPolicy policy;

	IOLockLock(lock);
	OSSharedPtr<OSDextCrash> crash = OSDextCrash::withTimestamp(timestamp);
	for (i = 0; i < crashes->getCount();) {
		OSDextCrash * current = OSDynamicCast(OSDextCrash, crashes->getObject(i));
		assert(current != NULL);
		if (current->getTimestamp() < lastTimestamp) {
			crashes->removeObject(i);
		} else {
			i++;
		}
	}

	crashes->setObject(crash);

	if (crashes->getCount() > kMaxDextCrashesInOneDay) {
		policy = kOSDextCrashPolicyReboot;
	} else {
		policy = kOSDextCrashPolicyNone;
	}

	IOLockUnlock(lock);

	return policy;
}

size_t
OSDextStatistics::getCrashCount()
{
	size_t result = 0;
	IOLockLock(lock);
	result = crashes->getCount();
	IOLockUnlock(lock);
	return result;
}

static int
sysctl_willuserspacereboot
(__unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
{
	int new_value = 0, old_value = get_system_inuserspacereboot(), changed = 0;
	int error = sysctl_io_number(req, old_value, sizeof(int), &new_value, &changed);
	if (error) {
		return error;
	}
	if (changed) {
		OSKext::setWillUserspaceReboot();
	}
	return 0;
}

static SYSCTL_PROC(_kern, OID_AUTO, willuserspacereboot,
    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED,
    NULL, 0, sysctl_willuserspacereboot, "I", "");
