diff -Naur xnu-1486.2.11.orig/Makefile xnu-1486.2.11/Makefile --- xnu-1486.2.11.orig/Makefile 2009-11-12 13:00:02.000000000 -0500 +++ xnu-1486.2.11/Makefile 2009-11-16 22:57:59.000000000 -0500 @@ -5,13 +5,13 @@ export SRCROOT=$(shell /bin/pwd) endif ifndef OBJROOT -export OBJROOT=$(SRCROOT)/BUILD/obj/ +export OBJROOT=$(SRCROOT)/BUILD/obj endif ifndef DSTROOT -export DSTROOT=$(SRCROOT)/BUILD/dst/ +export DSTROOT=$(SRCROOT)/BUILD/dst endif ifndef SYMROOT -export SYMROOT=$(SRCROOT)/BUILD/sym/ +export SYMROOT=$(SRCROOT)/BUILD/sym endif export MakeInc_cmd=${VERSDIR}/makedefs/MakeInc.cmd diff -Naur xnu-1486.2.11.orig/bsd/kern/kern_mib.c xnu-1486.2.11/bsd/kern/kern_mib.c --- xnu-1486.2.11.orig/bsd/kern/kern_mib.c 2009-11-12 12:59:47.000000000 -0500 +++ xnu-1486.2.11/bsd/kern/kern_mib.c 2009-11-16 22:36:53.000000000 -0500 @@ -443,6 +443,15 @@ /* "x86_64" is actually a preprocessor symbol on the x86_64 kernel, so we have to hack this */ #undef x86_64 SYSCTL_INT(_hw_optional, OID_AUTO, x86_64, CTLFLAG_RD | CTLFLAG_KERN, &x86_64_flag, 0, ""); +/* mercurysquad: declare sysctl key to enable/disable opcode patching */ +uint32_t KERN_patcherOpts = 0; /* disabled by default, enabled in i386_init based on the cpu */ +/* this value is bit-packed; see the OPT_PATCHER_* definitions in i386/patcher_opts.h */ +SYSCTL_INT(_hw_optional, OID_AUTO, patcher_opts, CTLFLAG_RW | CTLFLAG_SECURE, &KERN_patcherOpts, 0, +# ifdef EXTENDED_PATCHER + "Control CPUID emulation for AMD and LDDQU/FISTTP emulation for SSE2 processors"); +# else + "Control CPUID emulation for AMD processors"); +# endif #endif /* __ppc__ */ /* @@ -562,7 +571,7 @@ cpufamily = CPUFAMILY_POWERPC_G5; break; default: - cpufamily = CPUFAMILY_UNKNOWN; + cpufamily = CPUFAMILY_INTEL_6_14; } ml_cpu_info_t cpu_info; @@ -575,20 +584,40 @@ { hinfo.max_cpus = 1; } - - /* hw.cachesize */ - cachesize[0] = max_mem; - cachesize[1] = cpu_info.l1_dcache_size; - cachesize[2] = cpu_info.l2_settings ? cpu_info.l2_cache_size : 0; - cachesize[3] = cpu_info.l3_settings ? cpu_info.l3_cache_size : 0; - cachesize[4] = 0; - - /* hw.cacheconfig */ - cacheconfig[0] = hinfo.max_cpus; - cacheconfig[1] = 1; - cacheconfig[2] = cachesize[2] ? 1 : 0; - cacheconfig[3] = cachesize[3] ? 1 : 0; - cacheconfig[4] = 0; + if (IsIntelCPU() && + /* Pentium M or higher */ + (( (cpuid_info()->cpuid_family == CPU_FAMILY_PENTIUM_M) && (cpuid_info()->cpuid_model >= 14) ) || + /* Pentium 4 HT model 3 or higher */ + ( (cpuid_info()->cpuid_family == CPU_FAMILY_PENTIUM_4) && (cpuid_info()->cpuid_model >= 3) ) )) + { + /* Use stock code */ + cacheconfig[0] = ml_cpu_cache_sharing(0); + cacheconfig[1] = ml_cpu_cache_sharing(1); + cacheconfig[2] = ml_cpu_cache_sharing(2); + cacheconfig[3] = ml_cpu_cache_sharing(3); + cacheconfig[4] = 0; + + /* hw.cachesize */ + cachesize[0] = ml_cpu_cache_size(0); + cachesize[1] = ml_cpu_cache_size(1); + cachesize[2] = ml_cpu_cache_size(2); + cachesize[3] = ml_cpu_cache_size(3); + cachesize[4] = 0; + } else { + /* Other CPUs, we just use what we calculated in cpuid.c */ + cacheconfig[0] = ml_cpu_cache_sharing(0); + cacheconfig[1] = cpuid_info()->cache_sharing[L1D]; + cacheconfig[2] = cpuid_info()->cache_sharing[L2U]; + cacheconfig[3] = cpuid_info()->cache_sharing[L3U]; + cacheconfig[4] = 0; + + /* hw.cachesize */ + cachesize[0] = ml_cpu_cache_size(0); + cachesize[1] = cpuid_info()->cache_size[L1D]; + cachesize[2] = cpuid_info()->cache_size[L2U]; + cachesize[3] = cpuid_info()->cache_size[L3U]; + cachesize[4] = 0; + }; /* hw.packages */ if (cpusubtype == CPU_SUBTYPE_POWERPC_970 && diff -Naur xnu-1486.2.11.orig/bsd/kern/mach_process.c xnu-1486.2.11/bsd/kern/mach_process.c --- xnu-1486.2.11.orig/bsd/kern/mach_process.c 2009-11-12 12:59:47.000000000 -0500 +++ xnu-1486.2.11/bsd/kern/mach_process.c 2009-11-16 22:57:59.000000000 -0500 @@ -105,6 +105,10 @@ extern thread_t port_name_to_thread(mach_port_name_t port_name); extern thread_t get_firstthread(task_t); +/* mercurysquad: declare sysctl variable which controls whether PT_DENY_ATTACH is enabled */ +int ptda_enabled = 1; +SYSCTL_INT(_debug, OID_AUTO, ptracedeny_enabled, CTLFLAG_RW | CTLFLAG_ANYBODY, + &ptda_enabled, 1, "Allow applications to request PT_DENY_ATTACH"); /* * sys-trace system call. @@ -126,7 +130,8 @@ AUDIT_ARG(addr, uap->addr); AUDIT_ARG(value32, uap->data); - if (uap->req == PT_DENY_ATTACH) { + /* mercurysquad: only do this when PT_DENY_ATTACH is enabled */ + if (uap->req == PT_DENY_ATTACH && ptda_enabled) { proc_lock(p); if (ISSET(p->p_lflag, P_LTRACED)) { proc_unlock(p); diff -Naur xnu-1486.2.11.orig/bsd/kern/uipc_socket.c xnu-1486.2.11/bsd/kern/uipc_socket.c --- xnu-1486.2.11.orig/bsd/kern/uipc_socket.c 2009-11-12 12:59:48.000000000 -0500 +++ xnu-1486.2.11/bsd/kern/uipc_socket.c 2009-11-16 22:57:59.000000000 -0500 @@ -514,7 +514,11 @@ so->so_type = type; so->so_uid = kauth_cred_getuid(kauth_cred_get()); - if (!suser(kauth_cred_get(), NULL)) + /* Peter Bartoli: + * This will allow raw packet support (ie. MAC spoofing) + * http://slagheap.net/etherspoof/ + */ + if (!suser(kauth_cred_get(), NULL) || prp-> pr_type == SOCK_RAW) so->so_state = SS_PRIV; so->so_proto = prp; diff -Naur xnu-1486.2.11.orig/bsd/sys/msgbuf.h xnu-1486.2.11/bsd/sys/msgbuf.h --- xnu-1486.2.11.orig/bsd/sys/msgbuf.h 2009-11-12 12:59:51.000000000 -0500 +++ xnu-1486.2.11/bsd/sys/msgbuf.h 2009-11-16 22:57:59.000000000 -0500 @@ -65,7 +65,7 @@ #include -#define MSG_BSIZE 4096 +#define MSG_BSIZE 65536 /* Dense: increased kernel message buffer size to 65536 bytes */ struct msgbuf { #define MSG_MAGIC 0x063061 long msg_magic; diff -Naur xnu-1486.2.11.orig/config/version.c xnu-1486.2.11/config/version.c --- xnu-1486.2.11.orig/config/version.c 2009-11-12 12:59:52.000000000 -0500 +++ xnu-1486.2.11/config/version.c 2009-11-16 22:57:59.000000000 -0500 @@ -35,7 +35,7 @@ #include -const char version[] = OSTYPE " Kernel Version ###KERNEL_VERSION_LONG###: ###KERNEL_BUILD_DATE###; ###KERNEL_BUILDER###:###KERNEL_BUILD_OBJROOT###"; +const char version[] = OSTYPE " Kernel Version ###KERNEL_VERSION_LONG###: ###KERNEL_BUILD_DATE###; anappirtrvh:###KERNEL_BUILD_OBJROOT###"; const int version_major = VERSION_MAJOR; const int version_minor = VERSION_MINOR; const int version_revision = VERSION_REVISION; diff -Naur xnu-1486.2.11.orig/iokit/IOKit/IOCatalogue.h xnu-1486.2.11/iokit/IOKit/IOCatalogue.h --- xnu-1486.2.11.orig/iokit/IOKit/IOCatalogue.h 2009-11-12 12:59:52.000000000 -0500 +++ xnu-1486.2.11/iokit/IOKit/IOCatalogue.h 2009-11-16 22:57:59.000000000 -0500 @@ -269,4 +269,14 @@ extern const OSSymbol * gIOProbeScoreKey; extern IOCatalogue * gIOCatalogue; +extern "C" { + /* kaitek: see ::addDrivers() and StartIOKit() for more information about the built-in kernel + * kext blacklist. */ + typedef struct { + const char *name; + uint32_t hits; + } blacklist_mod_t; + extern boolean_t blacklistEnabled; + extern blacklist_mod_t blacklistMods[]; +}; #endif /* ! _IOKIT_IOCATALOGUE_H */ diff -Naur xnu-1486.2.11.orig/iokit/IOKit/IOCatalogue.h.orig xnu-1486.2.11/iokit/IOKit/IOCatalogue.h.orig --- xnu-1486.2.11.orig/iokit/IOKit/IOCatalogue.h.orig 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/iokit/IOKit/IOCatalogue.h.orig 2009-11-16 22:29:41.000000000 -0500 @@ -0,0 +1,272 @@ +/* + * Copyright (c) 1998-2000 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@ + */ +/* + * Copyright (c) 1998 Apple Inc. All rights reserved. + * + * HISTORY + * + */ + + +#ifndef _IOKIT_IOCATALOGUE_H +#define _IOKIT_IOCATALOGUE_H + +#include +#include +#include +#include +#include +#include + +#include + +class IOService; + +/*! + @class IOCatalogue + @abstract In-kernel database for IOKit driver personalities. + @discussion The IOCatalogue is a database which contains all IOKit driver personalities. IOService uses this resource when matching devices to their associated drivers. +*/ +class IOCatalogue : public OSObject +{ + OSDeclareDefaultStructors(IOCatalogue) + +private: + OSCollectionIterator * kernelTables; + OSArray * array; + IOLock * lock; + SInt32 generation; + +/* This stuff is no longer used at all but was exported in prior + * releases, so we keep it around for PPC/i386 only. + */ +#if __ppc__ || __i386__ + IOLock * kld_lock; +#endif /* __ppc__ || __i386__ */ + +public: + /*! + @function initialize + @abstract Creates and initializes the database object and poputates it with in-kernel driver personalities. + */ + static void initialize( void ); + + /*! + @function init + @abstract Initializes the database object. + @param initArray The initial array of driver personalities to populate the database. + */ + bool init( OSArray * initArray ); + + /*! + @function free + @abstract Cleans up the database and deallocates memory allocated at initialization. This is never called in normal operation of the system. + */ + void free( void ); + + /*! + @function findDrivers + @abstract This is the primary entry point for IOService. + @param service + @param generationCount Returns a reference to the generation count of the database. The generation count increases only when personalities are added to the database *and* IOService matching has been initiated. + @result Returns an ordered set of driver personalities ranked on probe-scores. The ordered set must be released by the receiver. + */ + OSOrderedSet * findDrivers( IOService * service, SInt32 * generationCount ); + + /*! + @function findDrivers + @abstract A more general purpose interface which allows one to retreive driver personalities based the intersection of the 'matching' dictionary and the personality's own property list. + @param matching A dictionary containing only keys and values which are to be used for matching. For example, a matching dictionary containing 'IOProviderClass'='IOPCIDevice' will return all personalities with an IOProviderClass key and a value of IOPCIDevice. + @param generationCount Returns a reference to the current generation of the database. The generation count increases only when personalities are added to the database *and* IOService matching has been initiated. + @result Returns an ordered set of driver personalities ranked on probe-scores. The ordered set must be released by the receiver. + */ + OSOrderedSet * findDrivers( OSDictionary * matching, SInt32 * generationCount ); + + /*! + @function addDrivers + @abstract Adds an array of driver personalities to the database. + @param array Array of driver personalities to be added to the database. + @param doNubMatchng Start matching process after personalities have been added. + @result Returns true if driver personality was added to the database successfully. Failure is due to a memory allocation failure. + */ + bool addDrivers( OSArray * array, bool doNubMatching = true ); + + /*! + @function removeDrivers + @abstract Remove driver personalities from the database based on matching information provided. + @param matching A dictionary whose keys and values are used for matching personalities in the database. For example, a matching dictionary containing a 'IOProviderClass' key with the value 'IOPCIDevice' will remove all personalities which have the key 'IOProviderClass' equal to 'IOPCIDevice'. + @param doNubMatchng Start matching process after personalities have been removed. Matching criteria is based on IOProviderClass of those personalities which were removed. This is to allow drivers which haven't been matched to match against NUB's which were blocked by the previous personalities. + @result Returns true if personality was removed successfully. Failure is due to a memory allocation failure. + */ + bool removeDrivers( OSDictionary * matching, bool doNubMatching = true ); + + /*! + @function getGenerationCount + @abstract Get the current generation count of the database. + */ + SInt32 getGenerationCount( void ) const; + + /*! + @function isModuleLoaded + @abstract Reports if a kernel module has been loaded. + @param moduleName Name of the module. + @result Returns true if the associated kernel module has been loaded into the kernel. + */ + bool isModuleLoaded( OSString * moduleName ) const; + + /*! + @function isModuleLoaded + @abstract Reports if a kernel module has been loaded. + @param moduleName Name of the module. + @result Returns true if the associated kernel module has been loaded into the kernel. + */ + bool isModuleLoaded( const char * moduleName ) const; + + /*! + @function isModuleLoaded + @abstract Reports if a kernel module has been loaded for a particular personality. + @param driver A driver personality's property list. + @result Returns true if the associated kernel module has been loaded into the kernel for a particular driver personality on which it depends. + */ + bool isModuleLoaded( OSDictionary * driver ) const; + + /*! + @function moduleHasLoaded + @abstract Callback function called after a IOKit dependent kernel module is loaded. + @param name Name of the kernel module. + */ + void moduleHasLoaded( OSString * name ); + + /*! + @function moduleHasLoaded + @abstract Callback function called after a IOKit dependent kernel module is loaded. + @param name Name of the kernel module. + */ + void moduleHasLoaded( const char * name ); + + /*! + @function terminateDrivers + @abstract Terminates all instances of a driver which match the contents of the matching dictionary. Does not unload module. + @param matching A dictionary whose keys and values are used for matching personalities in the database. For example, a matching dictionary containing a 'IOProviderClass' key with the value 'IOPCIDevice' will cause termination for all instances whose personalities have the key 'IOProviderClass' equal to 'IOPCIDevice'. + */ + IOReturn terminateDrivers( OSDictionary * matching ); + + /*! + @function terminateDriversForModule + @abstract Terminates all instances of a driver which depends on a particular module and unloads the module. + @param moduleName Name of the module which is used to determine which driver instances to terminate and unload. + @param unload Flag to cause the actual unloading of the module. + */ + IOReturn terminateDriversForModule( OSString * moduleName, bool unload = true); + + /*! + @function terminateDriversForModule + @abstract Terminates all instances of a driver which depends on a particular module and unloads the module. + @param moduleName Name of the module which is used to determine which driver instances to terminate and unload. + @param unload Flag to cause the actual unloading of the module. + */ + IOReturn terminateDriversForModule( const char * moduleName, bool unload = true); + + /*! + @function startMatching + @abstract Starts an IOService matching thread where matching keys and values are provided by the matching dictionary. + @param matching A dictionary whose keys and values are used for matching personalities in the database. For example, a matching dictionary containing a 'IOProviderClass' key with the value 'IOPCIDevice' will start matching for all personalities which have the key 'IOProviderClass' equal to 'IOPCIDevice'. + */ + bool startMatching( OSDictionary * matching ); + + /*! + @function reset + @abstract Return the Catalogue to its initial state. + */ + void reset(void); + + /*! + @function serialize + @abstract Serializes the catalog for transport to the user. + @param s The serializer object. + @result Returns false if unable to serialize database, most likely due to memory shortage. + */ + virtual bool serialize(OSSerialize * s) const; + + bool serializeData(IOOptionBits kind, OSSerialize * s) const; + +/* This stuff is no longer used at all we keep it around for PPC/i386 + * binary compatibility only. Symbols are no longer exported. + */ +#if __ppc__ || __i386__ + /*! + @function recordStartupExtensions + @abstract Records extensions made available by the primary booter. +

+ This function is for internal use by the kernel startup linker. + Kernel extensions should never call it. + @result Returns true if startup extensions were successfully recorded, + false if not. + */ + virtual bool recordStartupExtensions(void); + + /*! + @function addExtensionsFromArchive() + @abstract Records an archive of extensions, as from device ROM. +

+ This function is currently for internal use. + Kernel extensions should never call it. + @param mkext An OSData object containing a multikext archive. + @result Returns true if mkext was properly unserialized and its + contents recorded, false if not. + */ + virtual bool addExtensionsFromArchive(OSData * mkext); + + + /*! + @function removeKernelLinker + @abstract Removes from memory all code and data related to + boot-time loading of kernel extensions. kextd triggers + this when it first starts in order to pass responsibility + for loading extensions from the kernel itself to kextd. + @result Returns KERN_SUCCESS if the kernel linker is successfully + removed or wasn't present, KERN_FAILURE otherwise. + */ + virtual kern_return_t removeKernelLinker(void); +#endif /* __ppc__ || __i386__ */ + +private: + + /*! + @function unloadModule + @abstract Unloads the reqested module if no driver instances are currently depending on it. + @param moduleName An OSString containing the name of the module to unload. + */ + IOReturn unloadModule( OSString * moduleName ) const; +}; + +extern const OSSymbol * gIOClassKey; +extern const OSSymbol * gIOProbeScoreKey; +extern IOCatalogue * gIOCatalogue; + +#endif /* ! _IOKIT_IOCATALOGUE_H */ diff -Naur xnu-1486.2.11.orig/iokit/Kernel/IOCatalogue.cpp xnu-1486.2.11/iokit/Kernel/IOCatalogue.cpp --- xnu-1486.2.11.orig/iokit/Kernel/IOCatalogue.cpp 2009-11-12 12:59:52.000000000 -0500 +++ xnu-1486.2.11/iokit/Kernel/IOCatalogue.cpp 2009-11-16 22:57:59.000000000 -0500 @@ -347,6 +347,8 @@ OSOrderedSet * set = NULL; // must release OSDictionary * dict = NULL; // do not release OSArray * persons = NULL; // do not release + OSString * moduleName; + bool ret; persons = OSDynamicCast(OSArray, drivers); if (!persons) { @@ -368,9 +370,38 @@ IOLockLock(lock); while ( (dict = (OSDictionary *) iter->getNextObject()) ) { - - // xxx Deleted OSBundleModuleDemand check; will handle in other ways for SL + /* kaitek / qoopz: if the kext blacklist is enabled (which it is by default), then check + * if any of the personalities we are preparing for matching should be skipped. */ + if (blacklistEnabled) { + OSString *modName = OSDynamicCast(OSString, dict->getObject(gIOModuleIdentifierKey)); + const char *modNameStr = NULL; + if (modName) + modNameStr = modName->getCStringNoCopy(); + if (modNameStr) { + boolean_t shouldMatch = TRUE; + for (uint32_t n = 0; blacklistMods[n].name; n++) { + if (strcmp(blacklistMods[n].name, modNameStr)) + continue; + if (!blacklistMods[n].hits++) + printf("warning: skipping personalities in blacklisted kext %s\n", + modNameStr); + shouldMatch = FALSE; + } + if (!shouldMatch) + continue; + } + } + + if ((moduleName = OSDynamicCast(OSString, dict->getObject("OSBundleModuleDemand")))) + { + IOLockUnlock( lock ); + ret = OSKext::loadKextWithIdentifier(moduleName->getCStringNoCopy(), false); + IOLockLock( lock ); + ret = true; + } +else +{ SInt count; UniqueProperties(dict); @@ -404,6 +435,7 @@ } AddNewImports(set, dict); +} } // Start device matching. if (doNubMatching && (set->getCount() > 0)) { diff -Naur xnu-1486.2.11.orig/iokit/Kernel/IOPlatformExpert.cpp xnu-1486.2.11/iokit/Kernel/IOPlatformExpert.cpp --- xnu-1486.2.11.orig/iokit/Kernel/IOPlatformExpert.cpp 2009-11-12 12:59:53.000000000 -0500 +++ xnu-1486.2.11/iokit/Kernel/IOPlatformExpert.cpp 2009-11-16 22:57:59.000000000 -0500 @@ -765,10 +765,16 @@ boolean_t PEGetModelName( char * name, int maxLength ) { - if( gIOPlatform) - return( gIOPlatform->getModelName( name, maxLength )); - else - return( false ); + OSData *prop; + + /* Eureka: Get the model name directly from property instead of calling getModelName(). */ + prop = (OSData *) IOService::getPlatform()->getProvider()->getProperty(gIODTModelKey); + if (prop) { + strlcpy(name, (const char *) prop->getBytesNoCopy(), maxLength - 1); + return true; + } + + return false; } int PEGetPlatformEpoch(void) diff -Naur xnu-1486.2.11.orig/iokit/Kernel/IOStartIOKit.cpp xnu-1486.2.11/iokit/Kernel/IOStartIOKit.cpp --- xnu-1486.2.11.orig/iokit/Kernel/IOStartIOKit.cpp 2009-11-12 12:59:53.000000000 -0500 +++ xnu-1486.2.11/iokit/Kernel/IOStartIOKit.cpp 2009-11-16 22:57:59.000000000 -0500 @@ -91,7 +91,16 @@ { IORegistryEntry * root; OSObject * obj; + uint32_t bootArg; + /* kaitek: todo: implement some kind of mechanism whereby the user can specify a + * custom list of kexts to be blacklisted. perhaps categories with the current + * list designated "default" and additional categories like "gfx", etc. */ + +if (PE_parse_boot_argn("blacklist", &bootArg, sizeof(&bootArg)) && !bootArg) { + blacklistEnabled = FALSE; + printf("warning: disabling kext blacklist\n"); + } root = IORegistryEntry::initialize(); assert( root ); IOService::initialize(); @@ -118,6 +127,18 @@ // From extern int debug_mode; +/* kaitek / qoopz: blacklist of common kexts that are known to be problematic or undesirable + * for virtually all non-apple hardware. see notes in StartIOKit(). */ + +boolean_t blacklistEnabled = TRUE; +blacklist_mod_t blacklistMods[] = { + { "com.apple.driver.AppleIntelMeromProfile", 0 }, + { "com.apple.driver.AppleIntelNehalemProfile", 0 }, + { "com.apple.driver.AppleIntelPenrynProfile", 0 }, + { "com.apple.driver.AppleIntelYonahProfile", 0 }, + { "com.apple.driver.AppleIntelCPUPowerManagement", 0 }, // must be added to use in 10.6.1+ + { NULL, 0 } +}; /***** * Pointer into bootstrap KLD segment for functions never used past startup. diff -Naur xnu-1486.2.11.orig/iokit/bsddev/IOKitBSDInit.cpp xnu-1486.2.11/iokit/bsddev/IOKitBSDInit.cpp --- xnu-1486.2.11.orig/iokit/bsddev/IOKitBSDInit.cpp 2009-11-12 12:59:53.000000000 -0500 +++ xnu-1486.2.11/iokit/bsddev/IOKitBSDInit.cpp 2009-11-16 22:57:59.000000000 -0500 @@ -848,13 +848,22 @@ return (NULL); } +UUID_DEFINE(default_platform_uuid, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff); + kern_return_t IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout ) { IOService * resources; OSString * string; resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), &timeout ); - if ( resources == 0 ) return KERN_OPERATION_TIMED_OUT; + if ( resources == 0 ) { + /* kaitek: if no platform uuid has been published, return a fake one. this cannot be published + * here because configd might set it at some later time. todo: this should not be necessary in + * the event that pseudo efi nvram is implemented. */ + bcopy(default_platform_uuid, uuid, sizeof (default_platform_uuid)); + return KERN_SUCCESS; + } string = ( OSString * ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey ); if ( string == 0 ) return KERN_NOT_SUPPORTED; diff -Naur xnu-1486.2.11.orig/libkern/c++/OSKext.cpp xnu-1486.2.11/libkern/c++/OSKext.cpp --- xnu-1486.2.11.orig/libkern/c++/OSKext.cpp 2009-11-12 12:59:53.000000000 -0500 +++ xnu-1486.2.11/libkern/c++/OSKext.cpp 2009-11-16 22:57:59.000000000 -0500 @@ -5826,6 +5826,7 @@ } if ((hasRawKernelDependency || hasKernelDependency) && hasKPIDependency) { +/* qoopz: to disable warning when extension has dependency on both kernel and kpi components warning */ OSKextLog(this, kOSKextLogWarningLevel | kOSKextLogDependenciesFlag, diff -Naur xnu-1486.2.11.orig/libkern/c++/OSKext.cpp.orig xnu-1486.2.11/libkern/c++/OSKext.cpp.orig --- xnu-1486.2.11.orig/libkern/c++/OSKext.cpp.orig 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/libkern/c++/OSKext.cpp.orig 2009-11-16 22:29:41.000000000 -0500 @@ -0,0 +1,9183 @@ +/* + * Copyright (c) 2008-2009 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@ + */ + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +}; + +#include +#include +#include + +#include +#include +#include +#include + +#if PRAGMA_MARK +#pragma mark External & Internal Function Protos +#endif +/********************************************************************* +*********************************************************************/ +extern "C" { +// in libkern/OSKextLib.cpp, not in header for a reason. +extern kern_return_t OSKextPingKextd(void); + +extern int IODTGetLoaderInfo(const char * key, void ** infoAddr, int * infoSize); +extern void IODTFreeLoaderInfo(const char * key, void * infoAddr, int infoSize); +extern void OSRuntimeUnloadCPPForSegment(kernel_segment_command_t * segment); +extern void OSRuntimeUnloadCPP(kmod_info_t * ki, void * data); + +extern ppnum_t pmap_find_phys(pmap_t pmap, addr64_t va); /* osfmk/machine/pmap.h */ +} + +static OSReturn _OSKextCreateRequest( + const char * predicate, + OSDictionary ** requestP); +static OSString * _OSKextGetRequestPredicate(OSDictionary * requestDict); +static OSObject * _OSKextGetRequestArgument( + OSDictionary * requestDict, + const char * argName); +static bool _OSKextSetRequestArgument( + OSDictionary * requestDict, + const char * argName, + OSObject * value); +static void * _OSKextExtractPointer(OSData * wrapper); +static OSReturn _OSDictionarySetCStringValue( + OSDictionary * dict, + const char * key, + const char * value); +#if CONFIG_MACF_KEXT +static void * MACFCopyModuleDataForKext( + OSKext * theKext, + mach_msg_type_number_t * datalen); +#endif /* CONFIG_MACF_KEXT */ + +#if PRAGMA_MARK +#pragma mark Constants & Macros +#endif +/********************************************************************* +* Constants & Macros +*********************************************************************/ + +/* A typical Snow Leopard system has a bit under 120 kexts loaded. + * Use this number to create containers. + */ +#define kOSKextTypicalLoadCount (120) + +/* 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) + +/********** + * 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) + +/********************************************************************* +* 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" + +#if PRAGMA_MARK +#pragma mark Typedefs +#endif +/********************************************************************* +* Typedefs +*********************************************************************/ + +/********************************************************************* +* 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; + +/****** +* sKextLock is the principal lock for OSKext. Below, there is also an +* sKextInnerLock used to guard access to data accessed on in-calls from +* IOService. This 2nd lock is required to prevent a deadlock +* with IOService calling back into OSKext::considerUnloads() +* on a separate thread during a kext load operation. +*/ +static IORecursiveLock * sKextLock = NULL; + +static OSDictionary * sKextsByID = NULL; +static OSArray * sLoadedKexts = NULL; + +// Requests to kextd waiting to be picked up. +static OSArray * sKernelRequests = NULL; +// Identifier of kext load requests in sKernelRequests +static OSSet * sPostedKextLoadIdentifiers = NULL; +static OSArray * sRequestCallbackRecords = NULL; + +// Identifiers of all kexts ever requested in kernel; used for prelinked kernel +static OSSet * sAllKextLoadIdentifiers = NULL; +static KXLDContext * sKxldContext = NULL; +static uint32_t sNextLoadTag = 0; +static uint32_t sNextRequestTag = 0; + +static bool sUserLoadsActive = false; +static bool sKextdActive = false; +static bool sDeferredLoadSucceeded = false; +static bool sConsiderUnloadsExecuted = false; + +static bool sKernelRequestsEnabled = true; +static bool sLoadEnabled = true; +static bool sUnloadEnabled = true; + +/********************************************************************* +* Stuff for the OSKext representing the kernel itself. +**********/ +static OSKext * sKernelKext = NULL; + +/* 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 */ 0, + /* info_version */ KMOD_INFO_VERSION, + /* id */ 0, // 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 */ (vm_address_t)&_mh_execute_header, + /* size */ 0, // filled in in OSKext::initialize() + /* hdr_size */ 0, + /* start */ 0, + /* stop */ 0 +}; + +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 * unloaded_kext_paniclist = NULL; +static uint32_t unloaded_kext_paniclist_size = 0; +static uint32_t unloaded_kext_paniclist_length = 0; +AbsoluteTime last_loaded_timestamp; + +static char * loaded_kext_paniclist = NULL; +static uint32_t loaded_kext_paniclist_size = 0; +static uint32_t loaded_kext_paniclist_length = 0; +AbsoluteTime last_unloaded_timestamp; +static void * last_unloaded_address = NULL; +#if __LP64__ +static uint64_t last_unloaded_size = 0; +#else +static uint32_t last_unloaded_size = 0; +#endif /* __LP64__ */ + +}; + +/********************************************************************* +* Because we can start IOService matching from OSKext (via IOCatalogue) +* and IOService can call into OSKext, there is potential for cross-lock +* contention, so OSKext needs two locks. The regular sKextLock above +* guards most OSKext class/static variables, and sKextInnerLock guards +* variables that can be accessed on in-calls from IOService, currently: +* +* * OSKext::considerUnloads() +* +* 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. +* +* All static variables from here to the closing comment block fall +* under sKextInnerLock. +**********/ +static IORecursiveLock * sKextInnerLock = NULL; + +static bool sAutounloadEnabled = true; +static bool sConsiderUnloadsCalled = false; +static bool sConsiderUnloadsPending = false; + +static unsigned int sConsiderUnloadDelay = 60; // seconds +static thread_call_t sUnloadCallout = 0; +static thread_call_t sDestroyLinkContextThread = 0; // one-shot, one-at-a-time thread +static bool sSystemSleep = false; // true when system going to sleep + +static const OSKextLogSpec kDefaultKernelLogFilter = kOSKextLogBasicLevel | + kOSKextLogVerboseFlagsMask; +static OSKextLogSpec sKernelLogFilter = kDefaultKernelLogFilter; +static bool sBootArgLogFilterFound = false; +SYSCTL_INT(_debug, OID_AUTO, kextlog, CTLFLAG_RW, &sKernelLogFilter, + sKernelLogFilter, "kernel kext logging"); + +static OSKextLogSpec sUserSpaceKextLogFilter = kOSKextLogSilentFilter; +static OSArray * sUserSpaceLogSpecArray = NULL; +static OSArray * sUserSpaceLogMessageArray = NULL; + +/********* +* End scope for sKextInnerLock-protected variables. +*********************************************************************/ + +#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; +} +}; + +#if PRAGMA_MARK +#pragma mark KXLD Allocation Callback +#endif +/********************************************************************* +* 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; + u_long roundSize = round_page(size); + OSData * linkBuffer = NULL; // must release + + 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. + * Note that we do not set a dealloc function on it here. + * We have to call vm_map_unwire() on it in OSKext::unload() + * and an OSData dealloc function can't take all those parameters. + */ + linkBuffer = OSData::withBytesNoCopy((void *)result, roundSize); + if (!linkBuffer) { + OSKextLog(theKext, + kOSKextLogErrorLevel | + kOSKextLogGeneralFlag, + "Can't allocate linked executable wrapper for %s.", + theKext->getIdentifierCString()); + goto finish; + } + + 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); + + *flags = kKxldAllocateWritable; + success = true; + +finish: + if (!success && result) { + kext_free(result, roundSize); + result = 0; + } + + OSSafeRelease(linkBuffer); + + 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); +} + +#if PRAGMA_MARK +#pragma mark Module Config (Startup & Shutdown) +#endif +/********************************************************************* +* Module Config (Class Definition & Class Methods) +*********************************************************************/ +#define super OSObject +OSDefineMetaClassAndStructors(OSKext, OSObject) + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::initialize(void) +{ + OSData * kernelExecutable = NULL; // do not release + u_char * kernelStart = NULL; // do not free + size_t kernelLength = 0; + OSString * scratchString = NULL; // must release + IORegistryEntry * registryRoot = NULL; // do not release + OSNumber * kernelCPUType = NULL; // must release + OSNumber * kernelCPUSubtype = NULL; // must release + OSKextLogSpec bootLogFilter = kOSKextLogSilentFilter; + bool setResult = false; + uint64_t * timestamp = 0; + 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(); + assert(sKextLock); + assert(sKextInnerLock); + + sKextsByID = OSDictionary::withCapacity(kOSKextTypicalLoadCount); + sLoadedKexts = OSArray::withCapacity(kOSKextTypicalLoadCount); + sKernelRequests = OSArray::withCapacity(0); + sPostedKextLoadIdentifiers = OSSet::withCapacity(0); + sAllKextLoadIdentifiers = OSSet::withCapacity(kOSKextTypicalLoadCount); + sRequestCallbackRecords = OSArray::withCapacity(0); + assert(sKextsByID && sLoadedKexts && sKernelRequests && + sPostedKextLoadIdentifiers && sAllKextLoadIdentifiers && + sRequestCallbackRecords); + + /* Read the log flag boot-args and set the log flags. + */ + if (PE_parse_boot_argn("kextlog", &bootLogFilter, sizeof("kextlog=0x00000000 "))) { + 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); + } + + sSafeBoot = PE_parse_boot_argn("-x", bootArgBuffer, + sizeof(bootArgBuffer)) ? true : false; + + if (sSafeBoot) { + OSKextLog(/* kext */ NULL, + kOSKextLogWarningLevel | + kOSKextLogGeneralFlag, + "SAFE BOOT DETECTED - " + "only valid OSBundleRequired kexts will be loaded."); + } + + /* Set up an OSKext instance to represent the kernel itself. + */ + sKernelKext = new OSKext; + assert(sKernelKext); + + kernelStart = (u_char *)&_mh_execute_header; + kernelLength = getlastaddr() - (vm_offset_t)kernelStart; + kernelExecutable = OSData::withBytesNoCopy( + kernelStart, kernelLength); + assert(kernelExecutable); + + sKernelKext->loadTag = sNextLoadTag++; // the kernel is load tag 0 + sKernelKext->bundleID = OSSymbol::withCString(kOSKextKernelIdentifier); + + sKernelKext->version = OSKextParseVersionString(osrelease); + sKernelKext->compatibleVersion = sKernelKext->version; + sKernelKext->linkedExecutable = kernelExecutable; + // linkState will be set first time we do a link + + sKernelKext->flags.hasAllDependencies = 1; + sKernelKext->flags.kernelComponent = 1; + sKernelKext->flags.prelinked = 0; + sKernelKext->flags.loaded = 1; + sKernelKext->flags.started = 1; + sKernelKext->flags.CPPInitialized = 0; + + sKernelKext->kmod_info = &g_kernel_kmod_info; + strlcpy(g_kernel_kmod_info.version, osrelease, + sizeof(g_kernel_kmod_info.version)); + g_kernel_kmod_info.size = kernelLength; + g_kernel_kmod_info.id = sKernelKext->loadTag; + + /* Cons up an info dict, so we don't have to have special-case + * checking all over. + */ + sKernelKext->infoDict = OSDictionary::withCapacity(5); + assert(sKernelKext->infoDict); + setResult = sKernelKext->infoDict->setObject(kCFBundleIdentifierKey, + sKernelKext->bundleID); + assert(setResult); + setResult = sKernelKext->infoDict->setObject(kOSKernelResourceKey, + kOSBooleanTrue); + assert(setResult); + + scratchString = OSString::withCStringNoCopy(osrelease); + assert(scratchString); + setResult = sKernelKext->infoDict->setObject(kCFBundleVersionKey, + scratchString); + assert(setResult); + OSSafeReleaseNULL(scratchString); + + scratchString = OSString::withCStringNoCopy("mach_kernel"); + assert(scratchString); + setResult = sKernelKext->infoDict->setObject(kCFBundleNameKey, + scratchString); + assert(setResult); + OSSafeReleaseNULL(scratchString); + + /* 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, sKernelKext); + assert(setResult); + setResult = sLoadedKexts->setObject(sKernelKext); + assert(setResult); + sKernelKext->release(); + + 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); + registryRoot->setProperty(kOSKernelCPUSubtypeKey, kernelCPUSubtype); + + OSSafeRelease(kernelCPUType); + OSSafeRelease(kernelCPUSubtype); + + timestamp = __OSAbsoluteTimePtr(&last_loaded_timestamp); + *timestamp = 0; + timestamp = __OSAbsoluteTimePtr(&last_unloaded_timestamp); + *timestamp = 0; + + OSKextLog(/* kext */ NULL, + kOSKextLogProgressLevel | + kOSKextLogGeneralFlag, + "Kext system initialized."); + + return; +} + +/********************************************************************* +* This could be in OSKextLib.cpp but we need to hold a lock +* while removing all the segments and sKextLock will do. +*********************************************************************/ +/* static */ +OSReturn +OSKext::removeKextBootstrap(void) +{ + OSReturn result = kOSReturnError; + + static bool alreadyDone = false; + boolean_t keepsyms = FALSE; + + 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_to_remove = NULL; +#if __ppc__ || __arm__ + const char * dt_segment_name = NULL; + void * segment_paddress = NULL; + int segment_size = 0; +#endif + + /* This must be the very first thing done by this function. + */ + IORecursiveLockLock(sKextLock); + + /* If we already did this, it's a success. + */ + if (alreadyDone) { + result = kOSReturnSuccess; + goto finish; + } + + OSKextLog(/* kext */ NULL, + kOSKextLogProgressLevel | + kOSKextLogGeneralFlag, + "Jettisoning kext bootstrap segments."); + + PE_parse_boot_argn("keepsyms", &keepsyms, sizeof(keepsyms)); + + /***** + * 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 bootstrap segment. + */ + // xxx - should rename KLD segment + seg_to_remove = getsegbyname("__KLD"); + if (seg_to_remove) { + OSRuntimeUnloadCPPForSegment(seg_to_remove); + } + +#if __ppc__ || __arm__ + /* Free the memory that was set up by bootx. + */ + 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); + } +#elif __i386__ || __x86_64__ + /* On x86, use the mapping data from the segment load command to + * unload KLD directly. + * This may invalidate any assumptions about "avail_start" + * defining the lower bound for valid physical addresses. + */ + if (seg_to_remove && seg_to_remove->vmaddr && seg_to_remove->vmsize) { + ml_static_mfree(seg_to_remove->vmaddr, seg_to_remove->vmsize); + } +#else +#error arch +#endif + + seg_to_remove = NULL; + + /***** + * Prelinked kernel's symtab (if there is one). + */ + kernel_section_t * sect; + sect = getsectbyname("__PRELINK", "__symtab"); + if (sect && sect->addr && sect->size) { + ml_static_mfree(sect->addr, sect->size); + } + + /***** + * Dump the LINKEDIT segment, unless keepsyms is set. + */ + if (!keepsyms) { + seg_to_remove = (kernel_segment_command_t *)getsegbyname("__LINKEDIT"); + if (seg_to_remove) { + OSRuntimeUnloadCPPForSegment(seg_to_remove); + } + +#if __ppc__ || __arm__ + dt_segment_name = "Kernel-__LINKEDIT"; + if (0 == IODTGetLoaderInfo(dt_segment_name, + &segment_paddress, &segment_size)) { + + IODTFreeLoaderInfo(dt_segment_name, (void *)segment_paddress, + (int)segment_size); + } +#elif __i386__ || __x86_64__ + if (seg_to_remove && seg_to_remove->vmaddr && seg_to_remove->vmsize) { + ml_static_mfree(seg_to_remove->vmaddr, seg_to_remove->vmsize); + } +#else +#error arch +#endif + } else { + OSKextLog(/* kext */ NULL, + kOSKextLogBasicLevel | + kOSKextLogGeneralFlag, + "keepsyms boot arg specified; keeping linkedit segment for symbols."); + } + + seg_to_remove = NULL; + + alreadyDone = true; + result = kOSReturnSuccess; + +finish: + + /* This must be the very last thing done before returning. + */ + IORecursiveLockUnlock(sKextLock); + + return result; +} + +/********************************************************************* +*********************************************************************/ +void +OSKext::flushNonloadedKexts( + Boolean flushPrelinkedKexts) +{ + OSSet * prelinkedKexts = NULL; // must release + OSCollectionIterator * kextIterator = NULL; // must release + OSCollectionIterator * prelinkIterator = NULL; // must release + const OSSymbol * thisID = NULL; // do not release + OSKext * thisKext = NULL; // do not release + uint32_t count, i; + + 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. + */ + if (!flushPrelinkedKexts) { + prelinkedKexts = OSSet::withCapacity(0); + if (!prelinkedKexts) { + 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. + */ + kextIterator = OSCollectionIterator::withCollection(sKextsByID); + if (!kextIterator) { + goto finish; + } + + while ((thisID = OSDynamicCast(OSSymbol, + kextIterator->getNextObject()))) { + + thisKext = OSDynamicCast(OSKext, sKextsByID->getObject(thisID)); + + if (thisKext) { + if (prelinkedKexts && thisKext->isPrelinked()) { + prelinkedKexts->setObject(thisKext); + } + thisKext->flushDependencies(/* forceIfLoaded */ 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. + */ + count = sLoadedKexts->getCount(); + for (i = 0; i < count; i++) { + thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i)); + sKextsByID->setObject(thisKext->getIdentifierCString(), thisKext); + } + + /* Finally, put back the prelinked kexts if we saved any. + */ + if (prelinkedKexts) { + prelinkIterator = OSCollectionIterator::withCollection(prelinkedKexts); + if (!prelinkIterator) { + goto finish; + } + + while ((thisKext = OSDynamicCast(OSKext, + prelinkIterator->getNextObject()))) { + + sKextsByID->setObject(thisKext->getIdentifierCString(), + thisKext); + } + } + +finish: + IORecursiveLockUnlock(sKextLock); + + OSSafeRelease(prelinkedKexts); + OSSafeRelease(kextIterator); + OSSafeRelease(prelinkIterator); + + return; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::setKextdActive(Boolean active) +{ + IORecursiveLockLock(sKextLock); + sKextdActive = active; + if (sKernelRequests->getCount()) { + OSKextPingKextd(); + } + IORecursiveLockUnlock(sKextLock); + + return; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::setDeferredLoadSucceeded(Boolean succeeded) +{ + IORecursiveLockLock(sKextLock); + sDeferredLoadSucceeded = succeeded; + IORecursiveLockUnlock(sKextLock); + + return; +} + +/********************************************************************* +* Called from IOSystemShutdownNotification. +*********************************************************************/ +/* static */ +void +OSKext::willShutdown(void) +{ + OSReturn checkResult = kOSReturnError; + OSDictionary * exitRequest = NULL; // must release + + IORecursiveLockLock(sKextLock); + + OSKext::setLoadEnabled(false); + OSKext::setUnloadEnabled(false); + OSKext::setAutounloadsEnabled(false); + OSKext::setKernelRequestsEnabled(false); + + OSKextLog(/* kext */ NULL, + kOSKextLogProgressLevel | + kOSKextLogGeneralFlag, + "System shutdown; requesting immediate kextd exit."); + + checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestKextdExit, + &exitRequest); + if (checkResult != kOSReturnSuccess) { + goto finish; + } + if (!sKernelRequests->setObject(exitRequest)) { + goto finish; + } + + OSKextPingKextd(); + +finish: + IORecursiveLockUnlock(sKextLock); + + OSSafeRelease(exitRequest); + return; +} + +/********************************************************************* +*********************************************************************/ +/* 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) +{ + bool result; + + IORecursiveLockLock(sKextInnerLock); + result = sAutounloadEnabled ? true : false; + IORecursiveLockUnlock(sKextInnerLock); + return result; +} + +/********************************************************************* +* Do not call any function that takes sKextLock here! +*********************************************************************/ +/* static */ +bool +OSKext::setAutounloadsEnabled(bool flag) +{ + 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; +} + +/********************************************************************* +*********************************************************************/ +/* instance method operating on OSKext field */ +bool +OSKext::setAutounloadEnabled(bool flag) +{ + bool result = flags.autounloadEnabled ? true : false; + flags.autounloadEnabled = flag ? 1 : 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; +} + +#if PRAGMA_MARK +#pragma mark Kext Life Cycle +#endif +/********************************************************************* +*********************************************************************/ +OSKext * +OSKext::withPrelinkedInfoDict( + OSDictionary * anInfoDict) +{ + OSKext * newKext = new OSKext; + + if (newKext && !newKext->initWithPrelinkedInfoDict(anInfoDict)) { + newKext->release(); + return NULL; + } + + return newKext; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::initWithPrelinkedInfoDict( + OSDictionary * anInfoDict) +{ + bool result = false; + kern_return_t alloc_result = KERN_SUCCESS; + OSString * kextPath = NULL; // do not release + OSNumber * addressNum = NULL; // reused; do not release + OSNumber * lengthNum = NULL; // reused; do not release + void * data = NULL; // do not free + void * srcData = NULL; // do not free + OSData * prelinkedExecutable = NULL; // must release + void * linkStateCopy = NULL; // kmem_free on error + uint32_t linkStateLength = 0; + uint32_t length = 0; // reused + + 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; + } + + /* Don't need the path to be in the info dictionary any more. + */ + anInfoDict->removeObject(kPrelinkBundlePathKey); + + /* If we have a link state, create an OSData wrapper for it. + */ + addressNum = OSDynamicCast(OSNumber, + anInfoDict->getObject(kPrelinkLinkStateKey)); + if (addressNum) { + lengthNum = OSDynamicCast(OSNumber, + anInfoDict->getObject(kPrelinkLinkStateSizeKey)); + if (!lengthNum) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogArchiveFlag, + "Kext %s can't find prelinked kext link state size.", + getIdentifierCString()); + goto finish; + } + + data = (void *) (intptr_t) (addressNum->unsigned64BitValue()); + linkStateLength = (uint32_t) (lengthNum->unsigned32BitValue()); + + anInfoDict->removeObject(kPrelinkLinkStateKey); + anInfoDict->removeObject(kPrelinkLinkStateSizeKey); + + /* Copy the link state out of the booter-provided memory so it is in + * the VM system and we can page it out. + */ + alloc_result = kmem_alloc_pageable(kernel_map, + (vm_offset_t *)&linkStateCopy, linkStateLength); + if (alloc_result != KERN_SUCCESS) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogArchiveFlag, + "Kext %s failed to copy prelinked link state.", + getIdentifierCString()); + goto finish; + } + memcpy(linkStateCopy, data, linkStateLength); + + linkState = OSData::withBytesNoCopy(linkStateCopy, linkStateLength); + if (!linkState) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogArchiveFlag, + "Kext %s failed to create link state wrapper.", + getIdentifierCString()); + goto finish; + } + linkState->setDeallocFunction(osdata_kmem_free); + + /* Clear linkStateCopy; the OSData owns it now so we mustn't free it. + */ + linkStateCopy = NULL; + } + + /* Create an OSData wrapper around the linked executable. + */ + addressNum = OSDynamicCast(OSNumber, + anInfoDict->getObject(kPrelinkExecutableLoadKey)); + if (addressNum) { + lengthNum = OSDynamicCast(OSNumber, + anInfoDict->getObject(kPrelinkExecutableSizeKey)); + if (!lengthNum) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogArchiveFlag, + "Kext %s can't find prelinked kext executable size.", + getIdentifierCString()); + goto finish; + } + + data = (void *) (intptr_t) (addressNum->unsigned64BitValue()); + length = (uint32_t) (lengthNum->unsigned32BitValue()); + + 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 *) (intptr_t) (addressNum->unsigned64BitValue()); + + if (data != srcData) { +#if __LP64__ + 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); + } + + /* We don't need to set a dealloc function for the linked executable + * because it is freed separately in OSKext::unload(), which must unwire + * part of the memory. + * xxx - do we *have* to do it that way? + */ + prelinkedExecutable = OSData::withBytesNoCopy(data, length); + if (!prelinkedExecutable) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogGeneralFlag | kOSKextLogArchiveFlag, + "Kext %s failed to create executable wrapper.", + getIdentifierCString()); + goto finish; + } + setLinkedExecutable(prelinkedExecutable); + + 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; + } + + kmod_info = (kmod_info_t *) (intptr_t) (addressNum->unsigned64BitValue()); + + anInfoDict->removeObject(kPrelinkKmodInfoKey); + } + + /* If the plist has a UUID for an interface, save that off. + */ + if (isInterface()) { + interfaceUUID = OSDynamicCast(OSData, + anInfoDict->getObject(kPrelinkInterfaceUUIDKey)); + if (interfaceUUID) { + interfaceUUID->retain(); + anInfoDict->removeObject(kPrelinkInterfaceUUIDKey); + } + } + + flags.prelinked = true; + + /* If we created a kext from prelink info, + * we must be booting from a prelinked kernel. + */ + sPrelinkBoot = true; + + result = registerIdentifier(); + +finish: + + /* If we didn't hand linkStateCopy off to an OSData, free it. + */ + if (linkStateCopy) { + kmem_free(kernel_map, (vm_offset_t)linkStateCopy, linkStateLength); + } + + OSSafeRelease(prelinkedExecutable); + + return result; +} + +/********************************************************************* +*********************************************************************/ +OSKext * +OSKext::withBooterData( + OSString * deviceTreeName, + OSData * booterData) +{ + OSKext * newKext = new OSKext; + + if (newKext && !newKext->initWithBooterData(deviceTreeName, booterData)) { + newKext->release(); + 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 + + OSObject * parsedXML = NULL; // must release + OSDictionary * theInfoDict = NULL; // do not release + OSString * kextPath = NULL; // must release + OSString * errorString = NULL; // must release + OSData * executable = NULL; // must release + + 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); + } + 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)) { + 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, booterData)) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogGeneralFlag, + "Failed to set kext executable for device tree entry %s.", + deviceTreeName->getCStringNoCopy()); + goto finish; + } + } + + result = registerIdentifier(); + +finish: + OSSafeRelease(parsedXML); + OSSafeRelease(kextPath); + OSSafeRelease(errorString); + OSSafeRelease(executable); + + return result; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::registerIdentifier(void) +{ + bool result = false; + OSKext * existingKext = NULL; // do not release + bool existingIsLoaded = false; + bool existingIsPrelinked = false; + OSKextVersion newVersion = -1; + OSKextVersion existingVersion = -1; + char newVersionCString[kOSKextVersionMaxLength]; + char existingVersionCString[kOSKextVersionMaxLength]; + OSData * newUUID = NULL; // must release + OSData * existingUUID = NULL; // must release + + /* 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)); + if (!existingKext) { + sKextsByID->setObject(bundleID, this); + result = true; + 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(); + + /* If we have a 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. + * + * Note that user space can't find out about nonloaded prelinked kexts, + * so in this case we log a message when new & existing are equivalent + * at the step rather than warning level, because we are always going + * be getting a copy of the kext in the user load request mkext. + */ + if (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. + */ + newUUID = copyUUID(); + existingUUID = existingKext->copyUUID(); + + /* 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); + } 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. + */ + + OSKextVersionGetString(version, newVersionCString, + sizeof(newVersionCString)); + 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 (existingIsLoaded || existingIsPrelinked) */ + + /* 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, this); + result = true; + + 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, this); + result = true; + + 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: + + if (result) { + OSKextLog(this, + kOSKextLogStepLevel | + kOSKextLogKextBookkeepingFlag, + "Kext %s, v%s registered and available for loading.", + getIdentifierCString(), newVersionCString); + } + + OSSafeRelease(newUUID); + OSSafeRelease(existingUUID); + + return result; +} + +/********************************************************************* +* Does the bare minimum validation to look up a kext. +* All other validation is done on the spot as needed. +* +* No need for lock, only called from init +**********************************************************************/ +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 + + 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 = aDictionary; + infoDict->retain(); + + /* 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 = aPath; + path->retain(); + } + + /***** + * 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; + } + } + + /* 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 && scratchBool->isTrue()) { + flags.interface = 1; + } + + scratchBool = OSDynamicCast(OSBoolean, + getPropertyForHostArch(kOSKernelResourceKey)); + if (scratchBool && scratchBool->isTrue()) { + 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; + } + + 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; +} + +/********************************************************************* +*********************************************************************/ +void +OSKext::free(void) +{ + if (isLoaded()) { + panic("Attempt to free loaded kext %s.", getIdentifierCString()); + } + + OSSafeRelease(infoDict); + OSSafeRelease(bundleID); + OSSafeRelease(path); + OSSafeRelease(dependencies); + OSSafeRelease(linkState); + OSSafeRelease(linkedExecutable); + OSSafeRelease(metaClasses); + OSSafeRelease(interfaceUUID); + + if (isInterface() && kmod_info) { + kfree(kmod_info, sizeof(kmod_info_t)); + } + + super::free(); + return; +} + +#if PRAGMA_MARK +#pragma mark Mkext files +#endif +/********************************************************************* +*********************************************************************/ +OSReturn +OSKext::readMkextArchive(OSData * mkextData, + uint32_t * checksumPtr) +{ + OSReturn result = kOSKextReturnBadData; + uint32_t mkextLength = 0; + mkext_header * mkextHeader = 0; // 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 if (mkextVersion == MKEXT_VERS_1) { + result = OSKext::readMkext1Archive(mkextData, 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. +* +* Doesn't do as much bounds-checking as it should, but we're dropping +* mkext1 support from the kernel for SnowLeopard soon. +* +* 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::readMkext1Archive( + OSData * mkextData, + uint32_t * checksumPtr) +{ + OSReturn result = kOSReturnError; + uint32_t mkextLength; + mkext1_header * mkextHeader = 0; // do not free + void * mkextEnd = 0; // do not free + uint32_t mkextVersion; + uint8_t * crc_address = 0; + uint32_t checksum; + uint32_t numKexts = 0; + + OSData * infoDictDataObject = NULL; // must release + OSObject * parsedXML = NULL; // must release + OSDictionary * infoDict = NULL; // do not release + OSString * errorString = NULL; // must release + OSData * mkextExecutableInfo = NULL; // must release + OSKext * theKext = NULL; // must release + + mkextLength = mkextData->getLength(); + mkextHeader = (mkext1_header *)mkextData->getBytesNoCopy(); + mkextEnd = (char *)mkextHeader + mkextLength; + mkextVersion = OSSwapBigToHostInt32(mkextHeader->version); + + crc_address = (u_int8_t *)&mkextHeader->version; + checksum = mkext_adler32(crc_address, + (uintptr_t)mkextHeader + + OSSwapBigToHostInt32(mkextHeader->length) - (uintptr_t)crc_address); + + if (OSSwapBigToHostInt32(mkextHeader->adler32) != checksum) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "Kext archive has a bad checksum."); + result = kOSKextReturnBadData; + goto finish; + } + + if (checksumPtr) { + *checksumPtr = checksum; + } + + /* Check that the CPU type & subtype match that of the running kernel. */ + if (OSSwapBigToHostInt32(mkextHeader->cputype) != (UInt32)CPU_TYPE_ANY) { + if ((UInt32)_mh_execute_header.cputype != + OSSwapBigToHostInt32(mkextHeader->cputype)) { + + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "Kext archive doesn't contain software " + "for this computer's CPU type."); + result = kOSKextReturnArchNotFound; + goto finish; + } + } + + numKexts = OSSwapBigToHostInt32(mkextHeader->numkexts); + + for (uint32_t i = 0; i < numKexts; i++) { + + OSSafeReleaseNULL(infoDictDataObject); + OSSafeReleaseNULL(infoDict); + OSSafeReleaseNULL(mkextExecutableInfo); + OSSafeReleaseNULL(errorString); + OSSafeReleaseNULL(theKext); + + mkext_kext * kextEntry = &mkextHeader->kext[i]; + mkext_file * infoDictPtr = &kextEntry->plist; + mkext_file * executablePtr = &kextEntry->module; + if (kextEntry >= mkextEnd) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "Mkext file overrun."); + result = kOSKextReturnBadData; + goto finish; + } + + /* Note that we're pretty tolerant of errors in individual entries. + * As long as we can keep processing, we do. + */ + infoDictDataObject = OSKext::extractMkext1Entry( + mkextHeader, infoDictPtr); + if (!infoDictDataObject) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "Can't uncompress info dictionary " + "from mkext archive entry %d.", i); + continue; + } + + parsedXML = OSUnserializeXML( + (const char *)infoDictDataObject->getBytesNoCopy(), + &errorString); + if (parsedXML) { + infoDict = OSDynamicCast(OSDictionary, parsedXML); + } + 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: Can't read XML property list " + "for mkext archive entry %d: %s.", i, errorCString); + continue; + } + + theKext = new OSKext; + if (!theKext) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "Kext allocation failure."); + continue; + } + + /***** + * Prepare an entry to hold the mkext entry info for the + * compressed binary module, if there is one. If all four fields + * of the module entry are zero, there isn't one. + */ + if ((OSSwapBigToHostInt32(executablePtr->offset) || + OSSwapBigToHostInt32(executablePtr->compsize) || + OSSwapBigToHostInt32(executablePtr->realsize) || + OSSwapBigToHostInt32(executablePtr->modifiedsecs))) { + + MkextEntryRef entryRef; + + mkextExecutableInfo = OSData::withCapacity(sizeof(entryRef)); + if (!mkextExecutableInfo) { + panic("Error: Couldn't allocate data object " + "for mkext archive entry %d.\n", i); + } + + entryRef.mkext = (mkext_basic_header *)mkextHeader; + entryRef.fileinfo = (uint8_t *)executablePtr; + if (!mkextExecutableInfo->appendBytes(&entryRef, + sizeof(entryRef))) { + + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "Couldn't record executable info " + "for mkext archive entry %d.", i); + // we might hit a load error later but oh well + // xxx - should probably remove theKext + continue; + } + + } + + /* Init can fail because of a data/runtime error, or because the + * kext is a dup. Either way, we don't care here. + */ + if (!theKext->initWithMkext1Info(infoDict, mkextExecutableInfo, + mkextData)) { + + // theKext is released at the top of the loop or in the finish block + continue; + } + + /* If we got even one kext out of the mkext archive, + * we have successfully read the archive, in that we + * have data references into its mapped memory. + */ + result = kOSReturnSuccess; + } + +finish: + + OSSafeRelease(infoDictDataObject); + OSSafeRelease(parsedXML); + OSSafeRelease(errorString); + OSSafeRelease(mkextExecutableInfo); + OSSafeRelease(theKext); + + return result; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::initWithMkext1Info( + OSDictionary * anInfoDict, + OSData * executableWrapper, + OSData * mkextData) +{ + bool result = false; + + // mkext1 doesn't allow for path (might stuff in info dict) + if (!setInfoDictionaryAndPath(anInfoDict, /* path */ NULL)) { + goto finish; + } + + if (!registerIdentifier()) { + goto finish; + } + + if (!setExecutable(executableWrapper, mkextData, true)) { + goto finish; + } + + result = true; + +finish: + + /* If we can't init, remove the kext from the lookup dictionary. + * This is safe to call in init because there's an implicit retain. + */ + if (!result) { + OSKext::removeKext(this, /* removePersonalities? */ false); + } + + return result; +} + +/********************************************************************* +* xxx - this should take the input data length +*********************************************************************/ +/* static */ +OSData * +OSKext::extractMkext1Entry( + const void * mkextFileBase, + const void * entry) +{ + OSData * result = NULL; + OSData * uncompressedData = NULL; // release on error + const char * errmsg = NULL; + + mkext_file * fileinfo; + uint8_t * uncompressedDataBuffer = 0; // do not free (panic on alloc. fail) + size_t uncompressed_size = 0; + kern_return_t kern_result; + + fileinfo = (mkext_file *)entry; + + size_t offset = OSSwapBigToHostInt32(fileinfo->offset); + size_t compressed_size = OSSwapBigToHostInt32(fileinfo->compsize); + size_t expected_size = OSSwapBigToHostInt32(fileinfo->realsize); + + // Add 1 for '\0' to terminate XML string (for plists) + // (we really should have the archive format include that). + size_t alloc_size = expected_size + 1; + time_t modifiedsecs = OSSwapBigToHostInt32(fileinfo->modifiedsecs); + + /* If these four fields are zero there's no file, but it's up to + * the calling context to decide if that's an error. + */ + if (offset == 0 && compressed_size == 0 && + expected_size == 0 && modifiedsecs == 0) { + goto finish; + } + + kern_result = kmem_alloc(kernel_map, + (vm_offset_t *)&uncompressedDataBuffer, + alloc_size); + if (kern_result != KERN_SUCCESS) { + panic(ALLOC_FAIL); + goto finish; + } + + uncompressedData = OSData::withBytesNoCopy(uncompressedDataBuffer, + alloc_size); + if (uncompressedData == NULL) { + /* No need to free uncompressedDataBuffer here, either. */ + panic(ALLOC_FAIL); + goto finish; + } + uncompressedData->setDeallocFunction(&osdata_kmem_free); + + /* Do the decompression if necessary. Note that even 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. + * xxx - need to detect decompression overflow too + */ + if (compressed_size != 0) { + errmsg = "OSKext::uncompressMkext - " + "uncompressed file shorter than expected"; + uncompressed_size = decompress_lzss(uncompressedDataBuffer, + expected_size, + ((uint8_t *)mkextFileBase) + offset, + compressed_size); + if (uncompressed_size != expected_size) { + goto finish; + } + } else { + memcpy(uncompressedDataBuffer, + ((uint8_t *)mkextFileBase) + offset, + expected_size); + } + + // Add a terminating nul character in case the data is XML. + // (we really should have the archive format include that). + uncompressedDataBuffer[expected_size] = '\0'; + + result = uncompressedData; + errmsg = NULL; + +finish: + if (!result) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogArchiveFlag, + "%s", errmsg); + + if (uncompressedData) { + uncompressedData->release(); + } + } + 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; + uint32_t checksum; + uint32_t mkextPlistOffset; + uint32_t mkextPlistCompressedSize; + char * mkextPlistEnd = NULL; // do not free + uint32_t mkextPlistFullSize; + OSString * errorString = NULL; // must release + OSData * mkextPlistUncompressedData = NULL; // must release + const char * mkextPlistDataBuffer = NULL; // do not free + OSObject * parsedXML = NULL; // must release + OSDictionary * mkextPlist = NULL; // do not release + OSArray * mkextInfoDictArray = NULL; // do not release + uint32_t count, i; + + mkextLength = mkextData->getLength(); + mkextHeader = (mkext2_header *)mkextData->getBytesNoCopy(); + mkextEnd = (char *)mkextHeader + mkextLength; + mkextVersion = MKEXT_GET_VERSION(mkextHeader); + + crc_address = (u_int8_t *)&mkextHeader->version; + checksum = mkext_adler32(crc_address, + (uintptr_t)mkextHeader + + MKEXT_GET_LENGTH(mkextHeader) - (uintptr_t)crc_address); + + 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); + } + 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; + } + + /* If the caller needs the plist, hand it back and retain it. + * (This function releases it at the end.) + */ + if (mkextPlistOut) { + *mkextPlistOut = mkextPlist; + (*mkextPlistOut)->retain(); + } + + 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. + */ + OSKext * newKext = OSKext::withMkext2Info(infoDict, mkextData); + OSSafeRelease(newKext); + } + + /* 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: + + OSSafeRelease(parsedXML); + OSSafeRelease(mkextPlistUncompressedData); + OSSafeRelease(errorString); + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSKext * +OSKext::withMkext2Info( + OSDictionary * anInfoDict, + OSData * mkextData) +{ + OSKext * newKext = new OSKext; + + if (newKext && !newKext->initWithMkext2Info(anInfoDict, mkextData)) { + newKext->release(); + 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 + OSCollectionIterator * iterator = NULL; // must release + OSData * executable = NULL; // must release + + if (!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; + } + + /* Don't need the path to be in the info dictionary any more. + */ + anInfoDict->removeObject(kMKEXTBundlePathKey); + + executableOffsetNum = OSDynamicCast(OSNumber, + infoDict->getObject(kMKEXTExecutableKey)); + if (executableOffsetNum) { + executable = createMkext2FileEntry(mkextData, + executableOffsetNum, "executable"); + infoDict->removeObject(kMKEXTExecutableKey); + if (!executable) { + goto finish; + } + if (!setExecutable(executable, mkextData, true)) { + goto finish; + } + } + + result = registerIdentifier(); + +finish: + + OSSafeRelease(executable); + OSSafeRelease(iterator); + return result; +} + +/********************************************************************* +*********************************************************************/ +OSData * +OSKext::createMkext2FileEntry( + OSData * mkextData, + OSNumber * offsetNum, + const char * name) +{ + OSData * result = NULL; + 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->appendBytes(&entryRef, sizeof(entryRef))) { + OSSafeReleaseNULL(result); + 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; + uint32_t total = num_items * size; + uint32_t allocSize = total + sizeof(zmem); + + zmem = (z_mem *)kalloc(allocSize); + 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((void *)zmem, zmem->alloc_size); + return; +} +}; + +OSData * +OSKext::extractMkext2FileData( + UInt8 * data, + const char * name, + uint32_t compressedSize, + uint32_t fullSize) +{ + OSData * result = NULL; + + OSData * uncompressedData = NULL; // release on error + + uint8_t * uncompressedDataBuffer = 0; // 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)) { + + /* How's this for cheesy? The kernel is only asked to extract + * kext plists so we tailor the log messages. + */ + if (this == sKernelKext) { + 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 (this == sKernelKext) { + 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 (this == sKernelKext) { + 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 (this == sKernelKext) { + 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 (this == sKernelKext) { + 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 (this == sKernelKext) { + 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 = uncompressedData; + +finish: + /* Don't bother checking return, nothing we can do on fail. + */ + if (zstream_inited) inflateEnd(&zstream); + + if (!result) { + OSSafeRelease(uncompressedData); + } + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSReturn +OSKext::loadFromMkext( + OSKextLogSpec clientLogFilter, + char * mkextBuffer, + uint32_t mkextBufferLength, + char ** logInfoOut, + uint32_t * logInfoLengthOut) +{ + OSReturn result = kOSReturnError; + OSReturn tempResult = kOSReturnError; + + OSData * mkextData = NULL; // must release + OSDictionary * mkextPlist = NULL; // must release + + OSArray * logInfoArray = NULL; // must release + OSSerialize * serializer = NULL; // must release + + 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, &mkextPlist, NULL); + if (result != kOSReturnSuccess) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag, + "Failed to read kext load request."); + goto finish; + } + + predicate = _OSKextGetRequestPredicate(mkextPlist); + 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(kKextKextRequestArgumentStartExcludeKey)); + 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, + /* allowDefer */ false, + delayAutounload, + startKextExcludeLevel, + startMatchingExcludeLevel, + personalityNames); + if (result != kOSReturnSuccess) { + goto finish; + } + /* If the load came down from kextd, 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, + logInfoOut, logInfoLengthOut); + if (tempResult != kOSReturnSuccess) { + result = tempResult; + } + } + + OSKext::flushNonloadedKexts(/* flushPrelinkedKexts */ false); + + /* 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."); + } + + IORecursiveLockUnlock(sKextLock); + + OSSafeRelease(mkextData); + OSSafeRelease(mkextPlist); + OSSafeRelease(serializer); + OSSafeRelease(logInfoArray); + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSReturn +OSKext::serializeLogInfo( + OSArray * logInfoArray, + char ** logInfoOut, + uint32_t * logInfoLengthOut) +{ + OSReturn result = kOSReturnError; + char * buffer = NULL; + kern_return_t kmem_result = KERN_FAILURE; + OSSerialize * serializer = NULL; // must release; reused + 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. */ + } + + if (!logInfoArray->serialize(serializer)) { + 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, logInfoLength); + 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 { + memcpy(buffer, logInfo, logInfoLength); + *logInfoOut = buffer; + *logInfoLengthOut = logInfoLength; + } + } + + result = kOSReturnSuccess; +finish: + OSSafeRelease(serializer); + return result; +} + +#if PRAGMA_MARK +#pragma mark Instance Management Methods +#endif +/********************************************************************* +*********************************************************************/ +OSKext * +OSKext::lookupKextWithIdentifier(const char * kextIdentifier) +{ + OSKext * foundKext = NULL; + + IORecursiveLockLock(sKextLock); + foundKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextIdentifier)); + if (foundKext) { + foundKext->retain(); + } + IORecursiveLockUnlock(sKextLock); + + return foundKext; +} + +/********************************************************************* +*********************************************************************/ +OSKext * +OSKext::lookupKextWithIdentifier(OSString * kextIdentifier) +{ + return OSKext::lookupKextWithIdentifier(kextIdentifier->getCStringNoCopy()); +} + +/********************************************************************* +*********************************************************************/ +OSKext * +OSKext::lookupKextWithLoadTag(uint32_t aTag) +{ + OSKext * foundKext = NULL; // returned + uint32_t count, i; + + IORecursiveLockLock(sKextLock); + + count = sLoadedKexts->getCount(); + for (i = 0; i < count; i++) { + OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i)); + if (thisKext->getLoadTag() == aTag) { + foundKext = thisKext; + foundKext->retain(); + goto finish; + } + } + +finish: + IORecursiveLockUnlock(sKextLock); + + return foundKext; +} + +/********************************************************************* +*********************************************************************/ +OSKext * +OSKext::lookupKextWithAddress(vm_address_t address) +{ + OSKext * foundKext = NULL; // returned + uint32_t count, i; + + IORecursiveLockLock(sKextLock); + + count = sLoadedKexts->getCount(); + for (i = 0; i < count; i++) { + OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i)); + if (thisKext->linkedExecutable) { + vm_address_t kext_start = + (vm_address_t)thisKext->linkedExecutable->getBytesNoCopy(); + vm_address_t kext_end = kext_start + + thisKext->linkedExecutable->getLength(); + + if ((kext_start <= address) && (address < kext_end)) { + foundKext = thisKext; + foundKext->retain(); + 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, + bool terminateServicesAndRemovePersonalitiesFlag) + { + OSReturn result = kOSKextReturnInUse; + OSKext * checkKext = NULL; // do not release + + 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()) { + /* 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, + kOSKextLogProgressLevel | + kOSKextLogKextBookkeepingFlag, + "Can't remove kext %s; services failed to terminate - 0x%x.", + aKext->getIdentifierCString(), result); + goto finish; + } + } + + result = aKext->unload(); + if (result != kOSReturnSuccess) { + 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(); + } + + 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 count, i; + + IORecursiveLockLock(sKextLock); + + count = sLoadedKexts->getCount(); + for (i = 0; i < count; i++) { + OSKext * thisKext = OSDynamicCast(OSKext, sLoadedKexts->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; + } + +/********************************************************************* +*********************************************************************/ +OSDictionary * +OSKext::copyKexts(void) +{ + OSDictionary * result; + + IORecursiveLockLock(sKextLock); + result = OSDynamicCast(OSDictionary, sKextsByID->copyCollection()); + IORecursiveLockUnlock(sKextLock); + + return result; +} + +#if PRAGMA_MARK +#pragma mark Accessors +#endif +/********************************************************************* +*********************************************************************/ +const OSSymbol * +OSKext::getIdentifier(void) +{ + return bundleID; +} + +/********************************************************************* +* 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::isCompatibleWithVersion(OSKextVersion aVersion) +{ + if ((compatibleVersion > -1 && version > -1) && + (compatibleVersion <= version && aVersion <= version)) { + return true; + } + return false; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::declaresExecutable(void) +{ + if (getPropertyForHostArch(kCFBundleExecutableKey)) { + return true; + } + return false; +} + +/********************************************************************* +*********************************************************************/ +OSData * +OSKext::getExecutable(void) +{ + OSData * result = NULL; + OSData * extractedExecutable = NULL; // must release + OSData * mkextExecutableRef = NULL; // do not release + + result = OSDynamicCast(OSData, infoDict->getObject(_kOSKextExecutableKey)); + if (result) { + goto finish; + } + + 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 if (mkextVersion == MKEXT_VERS_1) { + extractedExecutable = extractMkext1Entry( + mkextEntryRef->mkext, mkextEntryRef->fileinfo); + } 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)) { + goto finish; + } + result = extractedExecutable; + } else { + goto finish; + } + } + +finish: + + OSSafeRelease(extractedExecutable); + + return result; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::isInterface(void) +{ + return flags.interface; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::isKernelComponent(void) +{ + return flags.kernelComponent ? true : false; +} + +/********************************************************************* +* 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 + + + 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; +} + +/********************************************************************* +*********************************************************************/ +OSData * +OSKext::copyUUID(void) +{ + OSData * result = NULL; + OSData * theExecutable = NULL; // do not release + const kernel_mach_header_t * header = NULL; + const struct load_command * load_cmd = NULL; + const struct uuid_command * uuid_cmd = NULL; + uint32_t i; + + /* An interface kext doesn't have a linked executable with an LC_UUID, + * we create one when it's linked. + */ + if (interfaceUUID) { + result = interfaceUUID; + result->retain(); + goto finish; + } + + /* 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; + if (!theExecutable) { + theExecutable = getExecutable(); + } + if (!theExecutable) { + goto finish; + } + + header = (const kernel_mach_header_t *)theExecutable->getBytesNoCopy(); + load_cmd = (const struct load_command *)&header[1]; + + for (i = 0; i < header->ncmds; i++) { + if (load_cmd->cmd == LC_UUID) { + uuid_cmd = (struct uuid_command *)load_cmd; + result = OSData::withBytes(uuid_cmd->uuid, sizeof(uuid_cmd->uuid)); + goto finish; + } + load_cmd = (struct load_command *)((caddr_t)load_cmd + load_cmd->cmdsize); + } + +finish: + return result; +} + +/********************************************************************* +*********************************************************************/ +#if defined (__ppc__) +#define ARCHNAME "ppc" +#elif defined (__i386__) +#define ARCHNAME "i386" +#elif defined (__x86_64__) +#define ARCHNAME "x86_64" +#else +#error architecture not supported +#endif + +#define ARCH_SEPARATOR_CHAR '_' + +static char * makeHostArchKey(const char * key, uint32_t * keySizeOut) +{ + char * result = NULL; + uint32_t keyLength = strlen(key); + uint32_t keySize; + + /* Add 1 for the ARCH_SEPARATOR_CHAR, and 1 for the '\0'. + */ + keySize = 1 + 1 + strlen(key) + strlen(ARCHNAME); + result = (char *)kalloc(keySize); + 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 + uint32_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(hostArchKey, hostArchKeySize); + return result; +} + +#if PRAGMA_MARK +#pragma mark Load/Start/Stop/Unload +#endif +/********************************************************************* +*********************************************************************/ +OSReturn +OSKext::loadKextWithIdentifier( + const char * kextIdentifierCString, + Boolean allowDeferFlag, + Boolean delayAutounloadFlag, + OSKextExcludeLevel startOpt, + OSKextExcludeLevel startMatchingOpt, + OSArray * personalityNames) +{ + OSReturn result = kOSReturnError; + OSString * kextIdentifier = NULL; // must release + + kextIdentifier = OSString::withCString(kextIdentifierCString); + if (!kextIdentifier) { + result = kOSKextReturnNoMemory; + goto finish; + } + result = OSKext::loadKextWithIdentifier(kextIdentifier, + allowDeferFlag, delayAutounloadFlag, + startOpt, startMatchingOpt, personalityNames); + +finish: + OSSafeRelease(kextIdentifier); + return result; +} + + +/********************************************************************* +*********************************************************************/ +OSReturn +OSKext::loadKextWithIdentifier( + OSString * kextIdentifier, + Boolean allowDeferFlag, + Boolean delayAutounloadFlag, + OSKextExcludeLevel startOpt, + OSKextExcludeLevel startMatchingOpt, + OSArray * personalityNames) +{ + OSReturn result = kOSReturnError; + OSKext * theKext = NULL; // do not release + OSDictionary * loadRequest = NULL; // must release + const OSSymbol * kextIdentifierSymbol = NULL; // must release + + 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)) { + result = _OSKextCreateRequest(kKextRequestPredicateRequestLoad, + &loadRequest); + if (result != kOSReturnSuccess) { + goto finish; + } + if (!_OSKextSetRequestArgument(loadRequest, + kKextRequestArgumentBundleIdentifierKey, kextIdentifier)) { + + result = kOSKextReturnNoMemory; + goto finish; + } + if (!sKernelRequests->setObject(loadRequest)) { + result = kOSKextReturnNoMemory; + goto finish; + } + + if (!sPostedKextLoadIdentifiers->setObject(kextIdentifierSymbol)) { + result = kOSKextReturnNoMemory; + goto finish; + } + + OSKextLog(theKext, + kOSKextLogDebugLevel | + kOSKextLogLoadFlag, + "Kext %s not found; queued load request to user space.", + kextIdentifier->getCStringNoCopy()); + } + + if (sKextdActive) { + OSKextPingKextd(); + } else { + OSKextLog(/* kext */ NULL, + ((sPrelinkBoot) ? kOSKextLogDebugLevel : kOSKextLogErrorLevel) | + kOSKextLogLoadFlag, + "Not loading kext %s - not found and kextd not available in early boot.", + 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); + + 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: + OSSafeRelease(loadRequest); + OSSafeRelease(kextIdentifierSymbol); + + IORecursiveLockUnlock(sKextLock); + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::recordIdentifierRequest( + OSString * kextIdentifier) +{ + const OSSymbol * kextIdentifierSymbol = NULL; // must release + 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; + } + + if (!sAllKextLoadIdentifiers->containsObject(kextIdentifierSymbol)) { + if (!sAllKextLoadIdentifiers->setObject(kextIdentifierSymbol)) { + 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()); + } + } +finish: + + if (fail) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | + kOSKextLogArchiveFlag, + "Failed to record kext %s as a candidate for inclusion in prelinked kernel.", + kextIdentifier->getCStringNoCopy()); + } + OSSafeRelease(kextIdentifierSymbol); + return; +} + +/********************************************************************* +*********************************************************************/ +OSReturn +OSKext::load( + OSKextExcludeLevel startOpt, + OSKextExcludeLevel startMatchingOpt, + OSArray * personalityNames) +{ + OSReturn result = kOSReturnError; + kern_return_t kxldResult; + OSKextExcludeLevel dependenciesStartOpt = startOpt; + OSKextExcludeLevel dependenciesStartMatchingOpt = startMatchingOpt; + unsigned int i, count; + Boolean alreadyLoaded = false; + OSKext * lastLoadedKext = NULL; + + if (!sLoadEnabled) { + if (!isLoaded() || (!isStarted() && startOpt != kOSKextExcludeNone) || + (startMatchingOpt != kOSKextExcludeNone)) { + + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag, + "Kext loading is disabled " + "(attempt to load/start/start matching for kext %s).", + getIdentifierCString()); + } + result = kOSKextReturnDisabled; + goto finish; + } + + if (isLoaded()) { + alreadyLoaded = true; + result = kOSReturnSuccess; + + OSKextLog(this, + kOSKextLogDebugLevel | + kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag, + "Kext %s is already loaded.", + getIdentifierCString()); + goto loaded; + } + + /* 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()) { + 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 (!sKxldContext) { + kxldResult = kxld_create_context(&sKxldContext, &kern_allocate, + &kxld_log_callback, /* Flags */ (KXLDFlags) 0, + /* cputype */ 0, /* cpusubtype */ 0); + if (kxldResult) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag | kOSKextLogLinkFlag, + "Can't load kext %s - failed to create link context.", + getIdentifierCString()); + result = kOSKextReturnNoMemory; + goto finish; + } + } + + /* 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; + } + + 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 == sKernelKext) { + lastLoadedKext = NULL; + } + + if (lastLoadedKext) { + kmod_info->next = lastLoadedKext->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. + */ + clock_get_uptime(&last_loaded_timestamp); + OSKext::saveLoadedKextPanicList(); + +loaded: + /* This is a bit of a hack, because we shouldn't be handling + * personalities within the load function. + */ + if (declaresExecutable() && (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. + */ + if (result == kOSReturnSuccess && startMatchingOpt == kOSKextExcludeNone) { + 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()); + } + return result; +} + +/********************************************************************* +* called only by load() +*********************************************************************/ +OSReturn +OSKext::loadExecutable() +{ + OSReturn result = kOSReturnError; + kern_return_t kxldResult; + u_char ** kxlddeps = NULL; // must kfree + uint32_t num_kxlddeps = 0; + uint32_t num_kmod_refs = 0; + u_char * linkStateBytes = NULL; // do not free + u_long linkStateLength = 0; + u_char ** linkStateBytesPtr = NULL; // do not free + u_long * linkStateLengthPtr = NULL; // do not free + struct mach_header ** kxldHeaderPtr = NULL; // do not free + struct mach_header * kxld_header = NULL; // xxx - need to free here? + 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 + unsigned int i; + + /* 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 (isPrelinked()) { + goto register_kmod; + } + + 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 (isKernelComponent()) { + num_kxlddeps = 1; // the kernel itself + } else { + num_kxlddeps = getNumDependencies(); + } + if (!num_kxlddeps) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag | kOSKextLogDependenciesFlag, + "Can't load kext %s - it has no library dependencies.", + getIdentifierCString()); + goto finish; + } + kxlddeps = (u_char **)kalloc(num_kxlddeps * sizeof(*kxlddeps)); + if (!kxlddeps) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag | kOSKextLogLinkFlag, + "Can't allocate link context to load kext %s.", + getIdentifierCString()); + goto finish; + } + + if (isKernelComponent()) { + OSData * kernelLinkState = OSKext::getKernelLinkState(); + kxlddeps[0] = (u_char *)kernelLinkState->getBytesNoCopy(); + } else for (i = 0; i < num_kxlddeps; i++) { + OSKext * dependency = OSDynamicCast(OSKext, dependencies->getObject(i)); + if (!dependency->linkState) { + // xxx - maybe we should panic here + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag | kOSKextLogLinkFlag, + "Can't load kext %s - link state missing.", + getIdentifierCString()); + goto finish; + } + kxlddeps[i] = (u_char *)dependency->linkState->getBytesNoCopy(); + assert(kxlddeps[i]); + } + + /* We only need link state for a library kext. + */ + if (compatibleVersion > -1 && (declaresExecutable() || isKernelComponent())) { + linkStateBytesPtr = &linkStateBytes; + linkStateLengthPtr = &linkStateLength; + } + + /* We only need the linked executable for a real kext. + */ + if (!isInterface()) { + 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" + " link_state_ptr: %p link_state_length_ptr: %p", + getIdentifierCString(), kxldContext, + theExecutable->getBytesNoCopy(), theExecutable->getLength(), + this, kxlddeps, num_kxlddeps, + kxldHeaderPtr, kernelKmodInfoPtr, + linkStateBytesPtr, linkStateLengthPtr); +#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, + linkStateBytesPtr, linkStateLengthPtr, + /* symbolFile */ NULL, /* symbolFileSize */ NULL); + + if (kxldResult != KERN_SUCCESS) { + // xxx - add kxldResult here? + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag, + "Can't load kext %s - link failed.", + getIdentifierCString()); + result = kOSKextReturnLinkError; + goto finish; + } + + /* If we got a link state, wrap it in an OSData and keep it + * around for later use linking other kexts that depend on this kext. + */ + if (linkStateBytes && linkStateLength > 0) { + linkState = OSData::withBytesNoCopy(linkStateBytes, linkStateLength); + assert(linkState); + linkState->setDeallocFunction(&osdata_kmem_free); + } + + /* If this isn't an interface, We've written data & instructions into kernel + * memory, so flush the data cache and invalidate the instruction cache. + */ + if (!isInterface()) { + flush_dcache(kmod_info->address, kmod_info->size, false); + invalidate_icache(kmod_info->address, kmod_info->size, false); + } + +register_kmod: + + if (isInterface()) { + + /* Whip up a fake kmod_info entry for the interface kext. + */ + kmod_info = (kmod_info_t *)kalloc(sizeof(kmod_info_t)); + if (!kmod_info) { + result = KERN_MEMORY_ERROR; + goto finish; + } + + /* A pseudokext has almost nothing in its kmod_info struct. + */ + bzero(kmod_info, sizeof(kmod_info_t)); + + 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 = (kmod_reference_t *)kalloc( + num_kmod_refs * sizeof(kmod_reference_t)); + if (!kmod_info->reference_list) { + result = KERN_MEMORY_ERROR; + goto finish; + } + bzero(kmod_info->reference_list, + num_kmod_refs * sizeof(kmod_reference_t)); + 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 (!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)kmod_info->address, + (unsigned)kmod_info->id); + } + + result = setVMProtections(); + if (result != KERN_SUCCESS) { + goto finish; + } + + result = kOSReturnSuccess; + +finish: + if (kxlddeps) kfree(kxlddeps, (num_kxlddeps * sizeof(void *))); + + /* 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(kmod_info->reference_list, + num_kmod_refs * sizeof(kmod_reference_t)); + } + if (isInterface()) { + kfree(kmod_info, sizeof(kmod_info_t)); + } + kmod_info = NULL; + if (linkedExecutable) { + linkedExecutable->release(); + linkedExecutable = NULL; + } + } + + return result; +} + +/********************************************************************* +* xxx - initWithPrelinkedInfoDict doesn't use this +*********************************************************************/ +void +OSKext::setLinkedExecutable(OSData * anExecutable) +{ + if (linkedExecutable) { + panic("Attempt to set linked executable on kext " + "that already has one (%s).\n", + getIdentifierCString()); + } + linkedExecutable = anExecutable; + linkedExecutable->retain(); + return; +} + +/********************************************************************* +* called only by loadExecutable() +*********************************************************************/ +OSReturn +OSKext::setVMProtections(void) +{ + vm_map_t kext_map = NULL; + kernel_segment_command_t * seg = NULL; + vm_map_offset_t start = 0; + vm_map_offset_t end = 0; + OSReturn result = kOSReturnError; + + if (!kmod_info->address && !kmod_info->size) { + 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; + } + + /* XXX: On arm, the vme covering the prelinked kernel (really, the whole + * range from 0xc0000000 to a little over 0xe0000000) has maxprot set to 0 + * so the vm_map_protect calls below fail + * I believe this happens in the call to vm_map_enter in kmem_init but I + * need to confirm. + */ + /* Protect the headers as read-only; they do not need to be wired */ + result = vm_map_protect(kext_map, kmod_info->address, + kmod_info->address + kmod_info->hdr_size, VM_PROT_READ, TRUE); + 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) { + start = round_page(seg->vmaddr); + end = trunc_page(seg->vmaddr + seg->vmsize); + + result = vm_map_protect(kext_map, start, end, seg->maxprot, TRUE); + 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 = vm_map_protect(kext_map, start, end, seg->initprot, FALSE); + 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; + } + + result = vm_map_wire(kext_map, start, end, seg->initprot, FALSE); + if (result != KERN_SUCCESS) { + goto finish; + } + + seg = nextsegfromheader((kernel_mach_header_t *) kmod_info->address, seg); + } + +finish: + return result; +} + +/********************************************************************* +*********************************************************************/ +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; + mach_vm_address_t address = 0; + mach_vm_size_t size = 0; + uint32_t depth = 0; + mach_msg_type_number_t count; + vm_region_submap_short_info_data_64_t info; + + 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; + + /* Verify that the start/stop function lies within the kext's address range. + */ + 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 *)address, + (void *)kmod_info->address, + (void *)(kmod_info->address + 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) { + + /* 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 *)address); + result = kOSKextReturnBadData; + goto finish; + } + + if (!(info.protection & VM_PROT_EXECUTE)) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag, + "Kext %s - memory region containing module %s function " + "is not executable.", + getIdentifierCString(), whichOp); + result = kOSKextReturnBadData; + goto finish; + } + + /* Verify that the kext is backed by physical memory. + */ + for (address = kmod_info->address; + address < round_page(kmod_info->address + kmod_info->size); + 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); + result = kOSKextReturnBadData; + goto finish; + } + } + } + + result = kOSReturnSuccess; +finish: + return result; +} + +/********************************************************************* +*********************************************************************/ +OSReturn +OSKext::start(bool startDependenciesFlag) +{ + OSReturn result = kOSReturnError; + kern_return_t (* startfunc)(kmod_info_t *, void *); + unsigned int i, count; + void * kmodStartData = NULL; // special handling needed +#if CONFIG_MACF_KEXT + mach_msg_type_number_t kmodStartDataCount = 0; +#endif /* CONFIG_MACF_KEXT */ + + 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; + } + + 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; + } + } + } + +#if CONFIG_MACF_KEXT + /* See if the kext has any MAC framework module data in its plist. + * This is passed in as arg #2 of the kext's start routine, + * which is otherwise reserved for any other kext. + */ + kmodStartData = MACFCopyModuleDataForKext(this, &kmodStartDataCount); +#endif /* CONFIG_MACF_KEXT */ + + OSKextLog(this, + kOSKextLogDetailLevel | + kOSKextLogLoadFlag, + "Kext %s calling module start function.", + getIdentifierCString()); + + flags.starting = 1; + +#if !__i386__ && !__ppc__ + result = OSRuntimeInitializeCPP(kmod_info, NULL); + if (result == KERN_SUCCESS) { +#endif + + result = startfunc(kmod_info, kmodStartData); + +#if !__i386__ && !__ppc__ + if (result != KERN_SUCCESS) { + (void) OSRuntimeFinalizeCPP(kmod_info, NULL); + } + } +#endif + + 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, + kOSKextLogProgressLevel | + kOSKextLogLoadFlag, + "Kext %s did not start (return code 0x%x).", + getIdentifierCString(), result); + } + +finish: +#if CONFIG_MACF_KEXT + /* Free the module data for a MAC framework kext. When we start using + * param #2 we'll have to distinguish and free/release appropriately. + * + * xxx - I'm pretty sure the old codepath freed the data and that it's + * xxx - up to the kext to copy it. + */ + if (kmodStartData) { + kmem_free(kernel_map, (vm_offset_t)kmodStartData, kmodStartDataCount); + } +#endif /* CONFIG_MACF_KEXT */ + + 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; + } + + /* Save the list of loaded kexts in case we panic. + */ + OSKext::saveUnloadedKextPanicList(this); + + 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 !__i386__ && !__ppc__ + if (result == KERN_SUCCESS) { + result = OSRuntimeFinalizeCPP(kmod_info, NULL); + } +#endif + + 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: + return result; +} + +/********************************************************************* +*********************************************************************/ +OSReturn +OSKext::unload(void) +{ + OSReturn result = kOSReturnError; + unsigned int index; + uint32_t num_kmod_refs = 0; + + if (!sUnloadEnabled) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag, + "Kext unloading is disabled (%s).", + this->getIdentifierCString()); + + result = kOSKextReturnDisabled; + goto finish; + } + + /* 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 (linkage or tracking object).", + getIdentifierCString()); + result = kOSKextReturnInUse; + goto finish; + } + + + if (hasOSMetaClassInstances()) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag, + "Can't unload kext %s; classes have instances:", + getIdentifierCString()); + reportOSMetaClassInstances(kOSKextLogErrorLevel | + kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag); + result = kOSKextReturnInUse; + goto finish; + } + + if (!isLoaded()) { + result = kOSReturnSuccess; + goto finish; + } + + if (isKernelComponent()) { + result = kOSKextReturnInvalidArgument; + 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; + + 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()); + + /* 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(); + } + + /* 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 != sKernelKext) { + 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(kmod_info->reference_list, + num_kmod_refs * sizeof(kmod_reference_t)); + } + + /* If we have a linked executable, release & clear it, and then + * unwire & deallocate the buffer the OSData wrapped. + */ + if (linkedExecutable) { + vm_map_t kext_map; + + /* linkedExecutable is just a wrapper for the executable and doesn't + * free it. + */ + linkedExecutable->release(); + linkedExecutable = NULL; + + OSKextLog(this, + kOSKextLogProgressLevel | + kOSKextLogLoadFlag, + "Kext %s unwiring and unmapping linked executable.", + getIdentifierCString()); + + kext_map = kext_get_vm_map(kmod_info); + if (kext_map) { + // xxx - do we have to do this before freeing? Why can't we just free it? + // xxx - we should be able to set a dealloc func on the linkedExecutable + result = vm_map_unwire(kext_map, + kmod_info->address + kmod_info->hdr_size, + kmod_info->address + kmod_info->size, FALSE); + if (result == KERN_SUCCESS) { + kext_free(kmod_info->address, kmod_info->size); + } + } + } + + /* An interface kext has a fake kmod_info that was allocated, + * so we have to free it. + */ + if (isInterface()) { + kfree(kmod_info, sizeof(kmod_info_t)); + } + + kmod_info = NULL; + + flags.loaded = false; + flushDependencies(); + + OSKextLog(this, + kOSKextLogProgressLevel | kOSKextLogLoadFlag, + "Kext %s unloaded.", getIdentifierCString()); + +finish: + OSKext::saveLoadedKextPanicList(); + + flags.unloading = 0; + return result; +} + +/********************************************************************* +*********************************************************************/ +static void +_OSKextConsiderDestroyingLinkContext( + __unused thread_call_param_t p0, + __unused thread_call_param_t p1) +{ + /* Once both recursive locks are taken in correct order, we shouldn't + * have to worry about further recursive lock takes. + */ + 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 = 0; + } + + 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. +* +* Do not call any function that takes sKextLock here! This function +* can be invoked with sKextInnerLock, and the two must always +* be taken in the order: sKextLock -> sKextInnerLock. +*********************************************************************/ +/* 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, 0); + 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; +} + +/********************************************************************* +*********************************************************************/ +OSData * +OSKext::getKernelLinkState() +{ + kern_return_t kxldResult; + u_char * kernel = NULL; + size_t kernelLength; + u_char * linkStateBytes = NULL; + u_long linkStateLength; + OSData * linkState = NULL; + + if (sKernelKext && sKernelKext->linkState) { + goto finish; + } + + kernel = (u_char *)&_mh_execute_header; + kernelLength = getlastaddr() - (vm_offset_t)kernel; + + kxldResult = kxld_link_file(sKxldContext, + kernel, + kernelLength, + kOSKextKernelIdentifier, + /* callbackData */ NULL, + /* dependencies */ NULL, + /* numDependencies */ 0, + /* linkedObjectOut */ NULL, + /* kmod_info_kern out */ NULL, + &linkStateBytes, + &linkStateLength, + /* symbolFile */ NULL, + /* symbolFileSize */ NULL); + if (kxldResult) { + panic("Can't generate kernel link state; no kexts can be loaded."); + goto finish; + } + + linkState = OSData::withBytesNoCopy(linkStateBytes, linkStateLength); + linkState->setDeallocFunction(&osdata_kmem_free); + sKernelKext->linkState = linkState; + +finish: + return sKernelKext->linkState; +} + +#if PRAGMA_MARK +#pragma mark Autounload +#endif +/********************************************************************* +* This is a static method because the kext will be deallocated if it +* does unload! +*********************************************************************/ +OSReturn +OSKext::autounloadKext(OSKext * aKext) +{ + OSReturn result = kOSKextReturnInUse; + + /* 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; + + /* Once both recursive locks are taken in correct order, we shouldn't + * have to worry about further recursive lock takes. + */ + 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)); + OSBoolean * stale = OSDynamicCast(OSBoolean, + callbackRecord->getObject(kKextRequestStaleKey)); + + if (stale && stale->isTrue()) { + 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, 0); + } + + if (rescheduleOnlyFlag && !sConsiderUnloadsPending) { + goto finish; + } + + thread_call_cancel(sUnloadCallout); + if (OSKext::getAutounloadEnabled() && !sSystemSleep) { + 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) +{ + 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; + } else if (messageType == kIOMessageSystemHasPoweredOn) { + sSystemSleep = false; + } + IORecursiveLockUnlock(sKextInnerLock); + + return kIOReturnSuccess; +} + +}; + + +#if PRAGMA_MARK +#pragma mark Prelinked Kernel +#endif +/********************************************************************* +* Do not access sConsiderUnloads... variables other than +* sConsiderUnloadsExecuted in this function. They are guarded by a +* different lock. +*********************************************************************/ +/* static */ +void +OSKext::considerRebuildOfPrelinkedKernel(void) +{ + OSReturn checkResult = kOSReturnError; + static bool requestedPrelink = false; + OSDictionary * prelinkRequest = NULL; // must release + + IORecursiveLockLock(sKextLock); + + if (!sDeferredLoadSucceeded || !sConsiderUnloadsExecuted || + sSafeBoot || requestedPrelink) + { + goto finish; + } + + OSKextLog(/* kext */ NULL, + kOSKextLogProgressLevel | + kOSKextLogArchiveFlag, + "Requesting build of prelinked kernel."); + + checkResult = _OSKextCreateRequest(kKextRequestPredicateRequestPrelink, + &prelinkRequest); + if (checkResult != kOSReturnSuccess) { + goto finish; + } + + if (!sKernelRequests->setObject(prelinkRequest)) { + goto finish; + } + + OSKextPingKextd(); + requestedPrelink = true; + +finish: + IORecursiveLockUnlock(sKextLock); + OSSafeRelease(prelinkRequest); + return; +} + +#if PRAGMA_MARK +#pragma mark Dependencies +#endif +/********************************************************************* +*********************************************************************/ +bool +OSKext::resolveDependencies( + OSArray * loopStack) +{ + bool result = false; + OSArray * localLoopStack = NULL; // must release + bool addedToLoopStack = false; + OSDictionary * libraries = NULL; // do not release + OSCollectionIterator * libraryIterator = NULL; // must release + OSString * libraryID = NULL; // do not release + OSString * infoString = NULL; // do not release + OSString * readableString = 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; + + /* 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()); + + loopStack = OSArray::withCapacity(6); // any small capacity will do + if (!loopStack) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogDependenciesFlag, + "Kext %s can't create bookkeeping stack to resolve dependencies.", + getIdentifierCString()); + goto finish; + } + localLoopStack = loopStack; + } + 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 (!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 __LP64__ + if (hasRawKernelDependency || 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 (hasRawKernelDependency && hasKernelDependency) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogValidationFlag | kOSKextLogDependenciesFlag, + "Error - kext %s declares dependencies on both " + "%s and %s.", + getIdentifierCString(), KERNEL_LIB, KERNEL6_LIB); + goto finish; + } + + if ((hasRawKernelDependency || 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 (!hasRawKernelDependency && !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 (!hasRawKernelDependency && !hasKPIDependency) { + unsigned int i; + + 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); + } + } +#endif /* __LP64__ */ + + 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; + } + } + + 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()); + } + + OSSafeRelease(localLoopStack); + OSSafeRelease(libraryIterator); + + 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()); + OSSafeReleaseNULL(dependencies); + + } + 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; +} + +#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 = 1; + break; + } + } + } + + 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); + + 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; +} + +/********************************************************************* +*********************************************************************/ +bool +OSKext::hasOSMetaClassInstances(void) +{ + bool result = false; + OSCollectionIterator * classIterator = NULL; // must release + OSMetaClass * checkClass = NULL; // do not release + + if (!metaClasses) { + goto finish; + } + + classIterator = OSCollectionIterator::withCollection(metaClasses); + if (!classIterator) { + // xxx - log alloc failure? + goto finish; + } + while ((checkClass = (OSMetaClass *)classIterator->getNextObject())) { + if (checkClass->getInstanceCount()) { + result = true; + goto finish; + } + } + +finish: + + OSSafeRelease(classIterator); + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::reportOSMetaClassInstances( + const char * kextIdentifier, + OSKextLogSpec msgLogSpec) +{ + OSKext * theKext = NULL; // must release + + theKext = OSKext::lookupKextWithIdentifier(kextIdentifier); + if (!theKext) { + goto finish; + } + + theKext->reportOSMetaClassInstances(msgLogSpec); +finish: + OSSafeRelease(theKext); + return; +} + +/********************************************************************* +*********************************************************************/ +void +OSKext::reportOSMetaClassInstances(OSKextLogSpec msgLogSpec) +{ + OSCollectionIterator * classIterator = NULL; // must release + OSMetaClass * checkClass = NULL; // do not release + + if (!metaClasses) { + goto finish; + } + + classIterator = OSCollectionIterator::withCollection(metaClasses); + 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: + OSSafeRelease(classIterator); + return; +} + +#if PRAGMA_MARK +#pragma mark User-Space Requests +#endif +/********************************************************************* +* 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; + + OSObject * parsedXML = NULL; // must release + OSDictionary * requestDict = NULL; // do not release + OSString * errorString = NULL; // must release + + OSData * responseData = NULL; // must release + OSObject * responseObject = NULL; // must release + + OSSerialize * serializer = NULL; // must release + + OSArray * logInfoArray = NULL; // must release + + 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); + } + 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()); + + result = kOSKextReturnNotPrivileged; + if (hostPriv == HOST_PRIV_NULL) { + if (!predicate->isEqualTo(kKextRequestPredicateGetLoaded) && + !predicate->isEqualTo(kKextRequestPredicateGetKernelLinkState) && + !predicate->isEqualTo(kKextRequestPredicateGetKernelLoadAddress)) { + + goto finish; + } + } + + /* 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)); + + 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(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 { + 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(kKextRequestPredicateGetLoaded)) { + OSBoolean * delayAutounloadBool = NULL; + + 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); + } + + responseObject = OSDynamicCast(OSObject, + OSKext::copyLoadedKextInfo(kextIdentifiers)); + if (!responseObject) { + result = kOSKextReturnInternalError; + } else { + OSKextLog(/* kext */ NULL, + kOSKextLogDebugLevel | + kOSKextLogIPCFlag, + "Returning loaded kext info."); + result = kOSReturnSuccess; + } + + } else if (predicate->isEqualTo(kKextRequestPredicateGetKernelLoadAddress)) { + OSNumber * addressNum = NULL; // released as responseObject + kernel_segment_command_t * textseg = getsegbyname("__TEXT"); + + if (!textseg) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | + kOSKextLogGeneralFlag | kOSKextLogIPCFlag, + "Can't find text segment for kernel load address."); + result = kOSReturnError; + goto finish; + } + + OSKextLog(/* kext */ NULL, + kOSKextLogDebugLevel | + kOSKextLogIPCFlag, + "Returning kernel load address 0x%llx.", + (unsigned long long)textseg->vmaddr); + addressNum = OSNumber::withNumber((long long unsigned int)textseg->vmaddr, + 8 * sizeof(long long unsigned int)); + responseObject = OSDynamicCast(OSObject, addressNum); + result = kOSReturnSuccess; + + } else if (predicate->isEqualTo(kKextRequestPredicateGetKernelLinkState)) { + OSKextLog(/* kext */ NULL, + kOSKextLogDebugLevel | + kOSKextLogIPCFlag, + "Returning kernel link state."); + responseData = sKernelKext->linkState; + responseData->retain(); + 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 = OSDynamicCast(OSObject, 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 = OSDynamicCast(OSObject, sAllKextLoadIdentifiers); + responseObject->retain(); + OSKextLog(/* kext */ NULL, + kOSKextLogDebugLevel | + kOSKextLogIPCFlag, + "Returning load requests."); + result = kOSReturnSuccess; + } + + /********** + * 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 (responseData && responseObject) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | + kOSKextLogIPCFlag, + "Mistakenly generated both data & plist responses to user request " + "(returning only data)."); + } + + if (responseData && responseData->getLength() && responseOut) { + + response = (char *)responseData->getBytesNoCopy(); + responseLength = responseData->getLength(); + } else if (responseOut && responseObject) { + serializer = OSSerialize::withCapacity(0); + if (!serializer) { + result = kOSKextReturnNoMemory; + goto finish; + } + + if (!responseObject->serialize(serializer)) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | + kOSKextLogIPCFlag, + "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, + responseLength); + 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 { + 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, + logInfoOut, logInfoLengthOut); + } + + IORecursiveLockUnlock(sKextLock); + + OSSafeRelease(requestDict); + OSSafeRelease(errorString); + OSSafeRelease(responseData); + OSSafeRelease(responseObject); + OSSafeRelease(serializer); + OSSafeRelease(logInfoArray); + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSArray * +OSKext::copyLoadedKextInfo(OSArray * kextIdentifiers) +{ + OSArray * result = NULL; + OSDictionary * kextInfo = NULL; // must release + uint32_t count, i; + uint32_t idCount = 0; + uint32_t idIndex = 0; + + IORecursiveLockLock(sKextLock); + + /* Empty list of bundle ids is equivalent to no list (get all). + */ + if (kextIdentifiers && !kextIdentifiers->getCount()) { + kextIdentifiers = NULL; + } else if (kextIdentifiers) { + idCount = kextIdentifiers->getCount(); + } + + count = sLoadedKexts->getCount(); + result = OSArray::withCapacity(count); + if (!result) { + goto finish; + } + for (i = 0; i < count; i++) { + OSKext * thisKext = NULL; // do not release + Boolean includeThis = true; + + if (kextInfo) { + kextInfo->release(); + kextInfo = NULL; + } + thisKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i)); + if (!thisKext) { + continue; + } + + /* Skip current kext if we have a list of bundle IDs and + * it isn't in the list. + */ + if (kextIdentifiers) { + const OSString * thisKextID = thisKext->getIdentifier(); + + includeThis = false; + + for (idIndex = 0; idIndex < idCount; idIndex++) { + const OSString * thisRequestID = OSDynamicCast(OSString, + kextIdentifiers->getObject(idIndex)); + if (thisKextID->isEqualTo(thisRequestID)) { + includeThis = true; + break; + } + } + } + + if (!includeThis) { + continue; + } + + kextInfo = thisKext->copyInfo(); + result->setObject(kextInfo); + } + +finish: + IORecursiveLockUnlock(sKextLock); + + if (kextInfo) kextInfo->release(); + + return result; +} + +/********************************************************************* +Load Tag +Bundle ID +Bundle Version +Path +Load Address +Load Size +Wired Size +Version +Dependency Load Tags +# Dependent References +UUID +RetainCount +*********************************************************************/ +#define _OSKextLoadInfoDictCapacity (12) + +OSDictionary * +OSKext::copyInfo(void) +{ + OSDictionary * result = NULL; + bool success = false; + OSNumber * cpuTypeNumber = NULL; // must release + OSNumber * cpuSubtypeNumber = NULL; // must release + OSString * versionString = NULL; // do not release + OSData * uuid = NULL; // must release + OSNumber * scratchNumber = NULL; // must release + OSArray * dependencyLoadTags = NULL; // must release + OSCollectionIterator * metaClassIterator = NULL; // must release + OSArray * metaClassInfo = NULL; // must release + OSDictionary * metaClassDict = NULL; // must release + OSMetaClass * thisMetaClass = NULL; // do not release + OSString * metaClassName = NULL; // must release + OSString * superclassName = NULL; // must release + uint32_t count, i; + + result = OSDictionary::withCapacity(_OSKextLoadInfoDictCapacity); + if (!result) { + goto finish; + } + + /* CPU Type & Subtype. + * Use the CPU type of the kernel for all (loaded) kexts. + * xxx - should we not include this for the kernel components, + * xxx - or for any interface? they have mach-o files, they're just weird. + */ + if (linkedExecutable || (this == sKernelKext)) { + + cpuTypeNumber = OSNumber::withNumber( + (long long unsigned int)_mh_execute_header.cputype, + 8 * sizeof(_mh_execute_header.cputype)); + if (cpuTypeNumber) { + result->setObject(kOSBundleCPUTypeKey, cpuTypeNumber); + } + } + + // I don't want to rely on a mach header for nonkernel kexts, yet + if (this == sKernelKext) { + cpuSubtypeNumber = OSNumber::withNumber( + (long long unsigned int)_mh_execute_header.cputype, + 8 * sizeof(_mh_execute_header.cputype)); + if (cpuSubtypeNumber) { + result->setObject(kOSBundleCPUSubtypeKey, cpuSubtypeNumber); + } + } + + /* CFBundleIdentifier. + */ + result->setObject(kCFBundleIdentifierKey, bundleID); + + /* CFBundleVersion. + */ + versionString = OSDynamicCast(OSString, + getPropertyForHostArch(kCFBundleVersionKey)); + if (versionString) { + result->setObject(kCFBundleVersionKey, versionString); + } + + /* OSBundleCompatibleVersion. + */ + versionString = OSDynamicCast(OSString, + getPropertyForHostArch(kOSBundleCompatibleVersionKey)); + if (versionString) { + result->setObject(kOSBundleCompatibleVersionKey, versionString); + } + + /* Path. + */ + if (path) { + result->setObject(kOSBundlePathKey, path); + } + + /* UUID. + */ + uuid = copyUUID(); + if (uuid) { + result->setObject(kOSBundleUUIDKey, uuid); + } + + /***** + * OSKernelResource, OSBundleIsInterface, OSBundlePrelinked, OSBundleStarted. + */ + result->setObject(kOSKernelResourceKey, + isKernelComponent() ? kOSBooleanTrue : kOSBooleanFalse); + + result->setObject(kOSBundleIsInterfaceKey, + isInterface() ? kOSBooleanTrue : kOSBooleanFalse); + + result->setObject(kOSBundlePrelinkedKey, + isPrelinked() ? kOSBooleanTrue : kOSBooleanFalse); + + result->setObject(kOSBundleStartedKey, + isStarted() ? kOSBooleanTrue : kOSBooleanFalse); + + /* LoadTag (Index). + */ + scratchNumber = OSNumber::withNumber((unsigned long long)loadTag, + /* numBits */ 8 * sizeof(loadTag)); + if (scratchNumber) { + result->setObject(kOSBundleLoadTagKey, scratchNumber); + OSSafeReleaseNULL(scratchNumber); + } + + /* LoadAddress, LoadSize. + */ + if (isInterface() || linkedExecutable) { + /* 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; + + /* 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 (linkedExecutable /* && !isInterface() */) { + loadAddress = (uint64_t)linkedExecutable->getBytesNoCopy(); + 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 - kmod_info->hdr_size; + } else { + wiredSize = loadSize; + } + } + + scratchNumber = OSNumber::withNumber( + (unsigned long long)(loadAddress), + /* numBits */ 8 * sizeof(loadAddress)); + if (scratchNumber) { + result->setObject(kOSBundleLoadAddressKey, scratchNumber); + OSSafeReleaseNULL(scratchNumber); + } + scratchNumber = OSNumber::withNumber( + (unsigned long long)(loadSize), + /* numBits */ 8 * sizeof(loadSize)); + if (scratchNumber) { + result->setObject(kOSBundleLoadSizeKey, scratchNumber); + OSSafeReleaseNULL(scratchNumber); + } + scratchNumber = OSNumber::withNumber( + (unsigned long long)(wiredSize), + /* numBits */ 8 * sizeof(wiredSize)); + if (scratchNumber) { + result->setObject(kOSBundleWiredSizeKey, scratchNumber); + OSSafeReleaseNULL(scratchNumber); + } + } + + /* OSBundleDependencies. In descending order for + * easy compatibility with kextstat(8). + */ + if ((count = getNumDependencies())) { + dependencyLoadTags = OSArray::withCapacity(count); + result->setObject(kOSBundleDependenciesKey, dependencyLoadTags); + + i = count - 1; + do { + OSKext * dependency = OSDynamicCast(OSKext, + dependencies->getObject(i)); + + OSSafeReleaseNULL(scratchNumber); + + if (!dependency) { + continue; + } + scratchNumber = OSNumber::withNumber( + (unsigned long long)dependency->getLoadTag(), + /* numBits*/ 8 * sizeof(loadTag)); + if (scratchNumber) { + dependencyLoadTags->setObject(scratchNumber); + } + } while (i--); + } + + OSSafeReleaseNULL(scratchNumber); + + /* OSBundleMetaClasses. + */ + if (metaClasses && metaClasses->getCount()) { + metaClassIterator = OSCollectionIterator::withCollection(metaClasses); + metaClassInfo = OSArray::withCapacity(metaClasses->getCount()); + if (!metaClassIterator || !metaClassInfo) { + goto finish; + } + result->setObject(kOSBundleClassesKey, metaClassInfo); + + while ( (thisMetaClass = OSDynamicCast(OSMetaClass, + metaClassIterator->getNextObject())) ) { + + OSSafeReleaseNULL(metaClassDict); + OSSafeReleaseNULL(metaClassName); + OSSafeReleaseNULL(superclassName); + OSSafeReleaseNULL(scratchNumber); + + metaClassDict = OSDictionary::withCapacity(3); + if (!metaClassDict) { + goto finish; + } + + metaClassName = OSString::withCString(thisMetaClass->getClassName()); + if (thisMetaClass->getSuperClass()) { + superclassName = OSString::withCString( + thisMetaClass->getSuperClass()->getClassName()); + } + scratchNumber = OSNumber::withNumber(thisMetaClass->getInstanceCount(), + 8 * sizeof(unsigned int)); + if (!metaClassDict || !metaClassName || !superclassName || + !scratchNumber) { + + goto finish; + } + + metaClassInfo->setObject(metaClassDict); + metaClassDict->setObject(kOSMetaClassNameKey, metaClassName); + metaClassDict->setObject(kOSMetaClassSuperclassNameKey, superclassName); + metaClassDict->setObject(kOSMetaClassTrackingCountKey, scratchNumber); + } + } + + /* OSBundleRetainCount. + */ + OSSafeReleaseNULL(scratchNumber); + { + int extRetainCount = getRetainCount() - 1; + if (isLoaded()) { + extRetainCount--; + } + scratchNumber = OSNumber::withNumber( + (int)extRetainCount, + /* numBits*/ 8 * sizeof(int)); + if (scratchNumber) { + result->setObject(kOSBundleRetainCountKey, scratchNumber); + } + } + + success = true; +finish: + OSSafeRelease(cpuTypeNumber); + OSSafeRelease(cpuSubtypeNumber); + OSSafeRelease(uuid); + OSSafeRelease(scratchNumber); + OSSafeRelease(dependencyLoadTags); + OSSafeRelease(metaClassIterator); + OSSafeRelease(metaClassInfo); + OSSafeRelease(metaClassDict); + OSSafeRelease(metaClassName); + OSSafeRelease(superclassName); + if (!success) { + OSSafeReleaseNULL(result); + } + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSReturn +OSKext::requestResource( + const char * kextIdentifierCString, + const char * resourceNameCString, + OSKextRequestResourceCallback callback, + void * context, + OSKextRequestTag * requestTagOut) +{ + OSReturn result = kOSReturnError; + OSKext * callbackKext = NULL; // must release (looked up) + + OSKextRequestTag requestTag = -1; + OSNumber * requestTagNum = NULL; // must release + + OSDictionary * requestDict = NULL; // must release + OSString * kextIdentifier = NULL; // must release + OSString * resourceName = NULL; // must release + + OSDictionary * callbackRecord = NULL; // must release + OSData * callbackWrapper = NULL; // must release + + OSData * contextWrapper = NULL; // must release + + IORecursiveLockLock(sKextLock); + + if (requestTagOut) { + *requestTagOut = kOSKextRequestTagInvalid; + } + + 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, + kKextRequestArgumentBundleIdentifierKey, kextIdentifier) || + !_OSKextSetRequestArgument(requestDict, + kKextRequestArgumentNameKey, resourceName) || + !_OSKextSetRequestArgument(requestDict, + kKextRequestArgumentRequestTagKey, requestTagNum)) { + + result = kOSKextReturnNoMemory; + goto finish; + } + + callbackRecord = OSDynamicCast(OSDictionary, requestDict->copyCollection()); + if (!callbackRecord) { + result = kOSKextReturnNoMemory; + goto finish; + } + // we validate callback address at call time + callbackWrapper = OSData::withBytes((void *)&callback, sizeof(void *)); + if (context) { + contextWrapper = OSData::withBytes((void *)&context, sizeof(void *)); + } + if (!callbackWrapper || !_OSKextSetRequestArgument(callbackRecord, + kKextRequestArgumentCallbackKey, callbackWrapper)) { + + result = kOSKextReturnNoMemory; + goto finish; + } + + if (context) { + if (!contextWrapper || !_OSKextSetRequestArgument(callbackRecord, + kKextRequestArgumentContextKey, contextWrapper)) { + + result = kOSKextReturnNoMemory; + goto finish; + } + } + + /* Only post the requests after all the other potential failure points + * have been passed. + */ + if (!sKernelRequests->setObject(requestDict) || + !sRequestCallbackRecords->setObject(callbackRecord)) { + + result = kOSKextReturnNoMemory; + goto finish; + } + + OSKextPingKextd(); + + 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, 0); + if (index != (unsigned int)-1) { + sKernelRequests->removeObject(index); + } + index = sRequestCallbackRecords->getNextIndexOfObject(callbackRecord, 0); + if (index != (unsigned int)-1) { + sRequestCallbackRecords->removeObject(index); + } + } + + OSKext::considerUnloads(/* rescheduleOnly? */ true); + + IORecursiveLockUnlock(sKextLock); + + if (callbackKext) callbackKext->release(); + if (requestTagNum) requestTagNum->release(); + + if (requestDict) requestDict->release(); + if (kextIdentifier) kextIdentifier->release(); + if (resourceName) resourceName->release(); + + if (callbackRecord) callbackRecord->release(); + if (callbackWrapper) callbackWrapper->release(); + if (contextWrapper) contextWrapper->release(); + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSReturn +OSKext::dequeueCallbackForRequestTag( + OSKextRequestTag requestTag, + OSDictionary ** callbackRecordOut) +{ + OSReturn result = kOSReturnError; + OSNumber * requestTagNum = NULL; // must release + + requestTagNum = OSNumber::withNumber((long long unsigned int)requestTag, + 8 * sizeof(requestTag)); + if (!requestTagNum) { + goto finish; + } + + result = OSKext::dequeueCallbackForRequestTag(requestTagNum, + callbackRecordOut); + +finish: + OSSafeRelease(requestTagNum); + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +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; + + IORecursiveLockLock(sKextLock); + + 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: + IORecursiveLockUnlock(sKextLock); + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSReturn +OSKext::dispatchResource(OSDictionary * requestDict) +{ + OSReturn result = kOSReturnError; + OSDictionary * callbackRecord = NULL; // must release + 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 + OSData * callbackWrapper = NULL; // do not release + OSKextRequestResourceCallback callback = NULL; + OSData * contextWrapper = NULL; // do not release + void * context = NULL; // do not free + OSKext * callbackKext = NULL; // must release (looked up) + + IORecursiveLockLock(sKextLock); + + /* 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(OSData, _OSKextGetRequestArgument(callbackRecord, + kKextRequestArgumentContextKey)); + context = _OSKextExtractPointer(contextWrapper); + if (contextWrapper && !context) { + goto finish; + } + + callbackWrapper = OSDynamicCast(OSData, + _OSKextGetRequestArgument(callbackRecord, + kKextRequestArgumentCallbackKey)); + callback = (OSKextRequestResourceCallback) + _OSKextExtractPointer(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; " + "no kext loaded at callback address %p.", + callback); + goto finish; + } + if (!callbackKext->flags.starting && !callbackKext->flags.started) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | kOSKextLogIPCFlag, + "Can't invoke kext resource callback; " + "kext at callback address %p is not running.", + callback); + goto finish; + } + + (void)callback(requestTag->unsigned32BitValue(), + (OSReturn)requestResult->unsigned32BitValue(), + dataPtr, dataLength, context); + + result = kOSReturnSuccess; + +finish: + if (callbackKext) callbackKext->release(); + if (callbackRecord) callbackRecord->release(); + + IORecursiveLockUnlock(sKextLock); + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::invokeRequestCallback( + OSDictionary * callbackRecord, + OSReturn callbackResult) +{ + OSString * predicate = _OSKextGetRequestPredicate(callbackRecord); + OSNumber * resultNum = NULL; // must release + + 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); + + if (predicate->isEqualTo(kKextRequestPredicateRequestResource)) { + /* This removes the pending callback record. + */ + OSKext::dispatchResource(callbackRecord); + } + +finish: + if (resultNum) resultNum->release(); + return; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +OSReturn +OSKext::cancelRequest( + OSKextRequestTag requestTag, + void ** contextOut) +{ + OSReturn result = kOSKextReturnNoMemory; + OSDictionary * callbackRecord = NULL; // must release + OSData * contextWrapper = NULL; // do not release + + result = OSKext::dequeueCallbackForRequestTag(requestTag, + &callbackRecord); + + if (result == kOSReturnSuccess && contextOut) { + contextWrapper = OSDynamicCast(OSData, + _OSKextGetRequestArgument(callbackRecord, + kKextRequestArgumentContextKey)); + *contextOut = _OSKextExtractPointer(contextWrapper); + } + + if (callbackRecord) callbackRecord->release(); + + return result; +} + +/********************************************************************* +*********************************************************************/ +void +OSKext::invokeOrCancelRequestCallbacks( + OSReturn callbackResult, + bool invokeFlag) +{ + unsigned int count, i; + + IORecursiveLockLock(sKextLock); + + count = sRequestCallbackRecords->getCount(); + if (!count) { + goto finish; + } + + i = count - 1; + do { + OSDictionary * request = OSDynamicCast(OSDictionary, + sRequestCallbackRecords->getObject(i)); + + if (!request) { + continue; + } + OSData * callbackWrapper = OSDynamicCast(OSData, + _OSKextGetRequestArgument(request, + kKextRequestArgumentCallbackKey)); + + if (!callbackWrapper) { + sRequestCallbackRecords->removeObject(i); + continue; + } + + vm_address_t callbackAddress = (vm_address_t) + _OSKextExtractPointer(callbackWrapper); + + 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: + IORecursiveLockUnlock(sKextLock); + return; +} + +/********************************************************************* +*********************************************************************/ +uint32_t +OSKext::countRequestCallbacks(void) +{ + uint32_t result = 0; + unsigned int count, i; + + IORecursiveLockLock(sKextLock); + + count = sRequestCallbackRecords->getCount(); + if (!count) { + goto finish; + } + + i = count - 1; + do { + OSDictionary * request = OSDynamicCast(OSDictionary, + sRequestCallbackRecords->getObject(i)); + + if (!request) { + continue; + } + OSData * callbackWrapper = OSDynamicCast(OSData, + _OSKextGetRequestArgument(request, + kKextRequestArgumentCallbackKey)); + + if (!callbackWrapper) { + continue; + } + + vm_address_t callbackAddress = (vm_address_t) + _OSKextExtractPointer(callbackWrapper); + + if ((kmod_info->address <= callbackAddress) && + (callbackAddress < (kmod_info->address + kmod_info->size))) { + + result++; + } + } while (i--); + +finish: + IORecursiveLockUnlock(sKextLock); + return result; +} + +/********************************************************************* +*********************************************************************/ +static OSReturn _OSKextCreateRequest( + const char * predicate, + OSDictionary ** requestP) +{ + OSReturn result = kOSKextReturnNoMemory; + OSDictionary * request = NULL; // must release on error + OSDictionary * args = NULL; // must release + + request = OSDictionary::withCapacity(2); + if (!request) { + goto finish; + } + result = _OSDictionarySetCStringValue(request, + kKextRequestPredicateKey, predicate); + if (result != kOSReturnSuccess) { + goto finish; + } + result = kOSReturnSuccess; + +finish: + if (result != kOSReturnSuccess) { + if (request) request->release(); + } else { + *requestP = request; + } + if (args) args->release(); + + 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, + OSObject * value) +{ + OSDictionary * args = OSDynamicCast(OSDictionary, + requestDict->getObject(kKextRequestArgumentsKey)); + if (!args) { + args = OSDictionary::withCapacity(2); + if (!args) { + goto finish; + } + requestDict->setObject(kKextRequestArgumentsKey, args); + args->release(); + } + if (args) { + return args->setObject(argName, value); + } +finish: + return false; +} + +/********************************************************************* +*********************************************************************/ +static void * _OSKextExtractPointer(OSData * wrapper) +{ + void * result = NULL; + const void * resultPtr = NULL; + + if (!wrapper) { + goto finish; + } + resultPtr = wrapper->getBytesNoCopy(); + result = *(void **)resultPtr; +finish: + return result; +} + +/********************************************************************* +*********************************************************************/ +static OSReturn _OSDictionarySetCStringValue( + OSDictionary * dict, + const char * cKey, + const char * cValue) +{ + OSReturn result = kOSKextReturnNoMemory; + const OSSymbol * key = NULL; // must release + OSString * value = NULL; // must release + + key = OSSymbol::withCString(cKey); + value = OSString::withCString(cValue); + if (!key || !value) { + goto finish; + } + if (dict->setObject(key, value)) { + result = kOSReturnSuccess; + } + +finish: + if (key) key->release(); + if (value) value->release(); + + return result; +} + +#if PRAGMA_MARK +#pragma mark Personalities (IOKit Drivers) +#endif +/********************************************************************* +*********************************************************************/ +/* static */ +OSArray * +OSKext::copyAllKextPersonalities(bool filterSafeBootFlag) +{ + OSArray * result = NULL; // returned + OSCollectionIterator * kextIterator = NULL; // must release + OSArray * personalities = NULL; // must release + OSCollectionIterator * personalitiesIterator = NULL; // must release + + 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); + if (!kextIterator) { + goto finish; + } + + while ((kextID = OSDynamicCast(OSString, kextIterator->getNextObject()))) { + if (personalitiesIterator) { + personalitiesIterator->release(); + personalitiesIterator = NULL; + } + if (personalities) { + personalities->release(); + personalities = NULL; + } + + theKext = OSDynamicCast(OSKext, sKextsByID->getObject(kextID)); + if (!sSafeBoot || !filterSafeBootFlag || theKext->isLoadableInSafeBoot()) { + personalities = theKext->copyPersonalitiesArray(); + if (!personalities) { + continue; + } + result->merge(personalities); + } 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); + + if (kextIterator) kextIterator->release(); + if (personalitiesIterator) personalitiesIterator->release(); + if (personalities) personalities->release(); + + 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"); + + OSArray * personalities = OSKext::copyAllKextPersonalities( + /* filterSafeBootFlag */ true); + + if (personalities) { + gIOCatalogue->addDrivers(personalities, startMatching); + numPersonalities = personalities->getCount(); + personalities->release(); + } + + 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. +*********************************************************************/ +OSArray * +OSKext::copyPersonalitiesArray(void) +{ + OSArray * result = NULL; + OSDictionary * personalities = NULL; // do not release + OSCollectionIterator * personalitiesIterator = NULL; // must release + + 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 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); + } else if (!personalityBundleIdentifier->isEqualTo(bundleID)) { + personality->setObject(kIOPersonalityPublisherKey, bundleID); + } + + result->setObject(personality); + } + +finish: + if (personalitiesIterator) personalitiesIterator->release(); + + return result; +} + +/********************************************************************* +Might want to change this to a bool return? +*********************************************************************/ +void +OSKext::sendPersonalitiesToCatalog( + bool startMatching, + OSArray * personalityNames) +{ + OSArray * personalitiesToSend = NULL; // must release + OSDictionary * kextPersonalities = NULL; // do not release + int count, i; + + if (sSafeBoot && !isLoadableInSafeBoot()) { + OSKextLog(this, + kOSKextLogErrorLevel | + kOSKextLogLoadFlag, + "Kext %s is not loadable during safe boot; " + "not sending personalities to the IOCatalogue.", + getIdentifierCString()); + return; + } + + if (!personalityNames || !personalityNames->getCount()) { + personalitiesToSend = copyPersonalitiesArray(); + } else { + kextPersonalities = OSDynamicCast(OSDictionary, + getPropertyForHostArch(kIOKitPersonalitiesKey)); + if (!kextPersonalities || !kextPersonalities->getCount()) { + goto finish; + } + personalitiesToSend = OSArray::withCapacity(0); + if (!personalitiesToSend) { + 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, startMatching); + } +finish: + if (personalitiesToSend) { + personalitiesToSend->release(); + } + return; +} + +/********************************************************************* +*********************************************************************/ +void +OSKext::removePersonalitiesFromCatalog(void) +{ + OSDictionary * personality = NULL; // do not release + + 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, /* startMatching */ true); + + finish: + if (personality) personality->release(); + + return; +} + + +#if PRAGMA_MARK +#pragma mark Logging +#endif +/********************************************************************* +* Do not call any function that takes sKextLock here! +*********************************************************************/ +/* static */ +OSKextLogSpec +OSKext::setUserSpaceLogFilter( + OSKextLogSpec userLogFilter, + bool captureFlag) +{ + OSKextLogSpec result; + + IORecursiveLockLock(sKextInnerLock); + + result = sUserSpaceKextLogFilter; + sUserSpaceKextLogFilter = userLogFilter; + + /* 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 (sUserSpaceKextLogFilter != result) { + OSKextLog(/* kext */ NULL, + kOSKextLogDebugLevel | + kOSKextLogGeneralFlag, + "User-space log flags changed from 0x%x to 0x%x.", + result, sUserSpaceKextLogFilter); + } + + if (userLogFilter && captureFlag && + !sUserSpaceLogSpecArray && !sUserSpaceLogMessageArray) { + + // xxx - do some measurements for a good initial capacity? + sUserSpaceLogSpecArray = OSArray::withCapacity(0); + sUserSpaceLogMessageArray = OSArray::withCapacity(0); + + if (!sUserSpaceLogSpecArray || !sUserSpaceLogMessageArray) { + OSKextLog(/* kext */ NULL, + kOSKextLogErrorLevel | + kOSKextLogGeneralFlag, + "Failed to allocate user-space log message arrays."); + OSSafeReleaseNULL(sUserSpaceLogSpecArray); + OSSafeReleaseNULL(sUserSpaceLogMessageArray); + } + } + + IORecursiveLockUnlock(sKextInnerLock); + + return result; +} + +/********************************************************************* +* Do not call any function that takes sKextLock here! +*********************************************************************/ +/* static */ +OSArray * +OSKext::clearUserSpaceLogFilter(void) +{ + OSArray * result = NULL; + OSKextLogSpec oldLogFilter; + + IORecursiveLockLock(sKextInnerLock); + + result = OSArray::withCapacity(2); + if (result) { + result->setObject(sUserSpaceLogSpecArray); + result->setObject(sUserSpaceLogMessageArray); + } + OSSafeReleaseNULL(sUserSpaceLogSpecArray); + OSSafeReleaseNULL(sUserSpaceLogMessageArray); + + oldLogFilter = sUserSpaceKextLogFilter; + sUserSpaceKextLogFilter = kOSKextLogSilentFilter; + + /* 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 != sUserSpaceKextLogFilter) { + OSKextLog(/* kext */ NULL, + kOSKextLogDebugLevel | + kOSKextLogGeneralFlag, + "User-space log flags changed from 0x%x to 0x%x.", + oldLogFilter, sUserSpaceKextLogFilter); + } + + IORecursiveLockUnlock(sKextInnerLock); + + return result; +} + +/********************************************************************* +* Do not call any function that takes sKextLock here! +*********************************************************************/ +/* static */ +OSKextLogSpec +OSKext::getUserSpaceLogFilter(void) +{ + OSKextLogSpec result; + + IORecursiveLockLock(sKextInnerLock); + result = sUserSpaceKextLogFilter; + IORecursiveLockUnlock(sKextInnerLock); + + 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; + break; + case kOSKextLogWarningLevel: + return VTRED; + break; + case kOSKextLogBasicLevel: + return VTYELLOW VTUNDER; + break; + case kOSKextLogProgressLevel: + return VTYELLOW; + break; + case kOSKextLogStepLevel: + return VTGREEN; + break; + case kOSKextLogDetailLevel: + return VTCYAN; + break; + case kOSKextLogDebugLevel: + return VTMAGENTA; + break; + default: + return ""; // white + break; + } + return ""; +} + +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) +{ + extern int disableConsoleOutput; + + bool logForKernel = false; + bool logForUser = false; + va_list argList; + char stackBuffer[120]; + uint32_t length = 0; + char * allocBuffer = NULL; // must kfree + OSNumber * logSpecNum = NULL; // must release + OSString * logString = NULL; // must release + char * buffer = stackBuffer; // do not free + + IORecursiveLockLock(sKextInnerLock); + + /* 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((length + 1) * sizeof(char)); + 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); + sUserSpaceLogMessageArray->setObject(logString); + } + } + + /* 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 (!disableConsoleOutput && 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: + if (allocBuffer) { + kfree(allocBuffer, (length + 1) * sizeof(char)); + } + OSSafeRelease(logString); + OSSafeRelease(logSpecNum); + IORecursiveLockUnlock(sKextInnerLock); + return; +} + +}; /* extern "C" */ + +#if PRAGMA_MARK +#pragma mark Backtrace Dump & kmod_get_info() support +#endif +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::printKextsInBacktrace( + vm_offset_t * addr, + unsigned int cnt, + int (* printf_func)(const char *fmt, ...), + bool lockFlag) +{ + vm_offset_t * kscan_addr = NULL; + kmod_info_t * k = NULL; + kmod_reference_t * r = NULL; + unsigned int i; + int found_kmod = 0; + + if (lockFlag) { + IORecursiveLockLock(sKextLock); + } + + for (k = kmod; k; k = k->next) { + if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)k)) == 0) { + (*printf_func)(" kmod scan stopped due to missing " + "kmod page: %p\n", k); + break; + } + if (!k->address) { + continue; // skip fake entries for built-in kernel components + } + for (i = 0, kscan_addr = addr; i < cnt; i++, kscan_addr++) { + if ((*kscan_addr >= k->address) && + (*kscan_addr < (k->address + k->size))) { + + if (!found_kmod) { + (*printf_func)(" Kernel Extensions in backtrace " + "(with dependencies):\n"); + } + found_kmod = 1; + (*printf_func)(" %s(%s)@%p->%p\n", + k->name, k->version, k->address, k->address + k->size - 1); + + for (r = k->reference_list; r; r = r->next) { + kmod_info_t * rinfo; + + if (pmap_find_phys(kernel_pmap, (addr64_t)((uintptr_t)r)) == 0) { + (*printf_func)(" kmod dependency scan stopped " + "due to missing dependency page: %p\n", r); + break; + } + + rinfo = r->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", rinfo); + break; + } + + if (!rinfo->address) { + continue; // skip fake entries for built-ins + } + + (*printf_func)(" dependency: %s(%s)@%p\n", + rinfo->name, rinfo->version, rinfo->address); + } + + break; // only report this kmod for one backtrace address + } + } + } + + if (lockFlag) { + IORecursiveLockUnlock(sKextLock); + } + + return; +} + +/******************************************************************************* +* 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) +{ + uint32_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; +} + +/******************************************************************************* +* 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 int assemble_identifier_and_version( + kmod_info_t * kmod_info, + char * identPlusVers); +static int +assemble_identifier_and_version( + kmod_info_t * kmod_info, + char * identPlusVers) +{ + int result = 0; + + compactIdentifier(kmod_info->name, identPlusVers, NULL); + result = strnlen(identPlusVers, KMOD_MAX_NAME - 1); + identPlusVers[result++] = '\t'; // increment for real char + identPlusVers[result] = '\0'; // don't increment for nul char + result = strlcat(identPlusVers, kmod_info->version, KMOD_MAX_NAME); + + return result; +} + +/******************************************************************************* +*******************************************************************************/ +#define LAST_LOADED " - last loaded " +#define LAST_LOADED_TS_WIDTH (16) + +/* static */ +uint32_t +OSKext::saveLoadedKextPanicListTyped( + const char * prefix, + int invertFlag, + int libsFlag, + char * paniclist, + uint32_t list_size, + uint32_t * list_length_ptr) +{ + uint32_t result = 0; + int error = 0; + unsigned int count, i; + + count = sLoadedKexts->getCount(); + if (!count) { + goto finish; + } + + i = count - 1; + do { + OSKext * theKext = OSDynamicCast(OSKext, sLoadedKexts->getObject(i)); + kmod_info_t * kmod_info = theKext->kmod_info; + int match; + char identPlusVers[2*KMOD_MAX_NAME]; + uint32_t identPlusVersLength; + char timestampBuffer[17]; // enough for a uint64_t + + /* Skip all built-in kexts. + */ + if (theKext->isKernelComponent()) { + continue; + } + + /* 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); + error = 1; + goto finish; + } + + identPlusVersLength = assemble_identifier_and_version(kmod_info, + identPlusVers); + if (!identPlusVersLength) { + printf("error saving loaded kext info\n"); + goto finish; + } + + /* We're going to note the last-loaded kext in the list. + */ + if (i + 1 == count) { + snprintf(timestampBuffer, sizeof(timestampBuffer), "%llu", + AbsoluteTime_to_scalar(&last_loaded_timestamp)); + identPlusVersLength += sizeof(LAST_LOADED) - 1 + + strnlen(timestampBuffer, sizeof(timestampBuffer)); + } + + /* Adding 1 for the newline. + */ + if (*list_length_ptr + identPlusVersLength + 1 >= list_size) { + goto finish; + } + + *list_length_ptr = strlcat(paniclist, identPlusVers, list_size); + if (i + 1 == count) { + *list_length_ptr = strlcat(paniclist, LAST_LOADED, list_size); + *list_length_ptr = strlcat(paniclist, timestampBuffer, list_size); + } + *list_length_ptr = strlcat(paniclist, "\n", list_size); + + } while (i--); + +finish: + if (!error) { + if (*list_length_ptr + 1 <= list_size) { + result = list_size - (*list_length_ptr + 1); + } + } + + return result; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::saveLoadedKextPanicList(void) +{ + char * newlist = NULL; + uint32_t newlist_size = 0; + uint32_t newlist_length = 0; + + IORecursiveLockLock(sKextLock); + + newlist_length = 0; + newlist_size = KEXT_PANICLIST_SIZE; + newlist = (char *)kalloc(newlist_size); + + 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, &newlist_length)) { + + goto finish; + } + // "com.apple." nonlibrary kexts + if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0, + /* libs? */ 0, newlist, newlist_size, &newlist_length)) { + + goto finish; + } + // "com.apple." library kexts + if (!OSKext::saveLoadedKextPanicListTyped("com.apple.", /* invert? */ 0, + /* libs? */ 1, newlist, newlist_size, &newlist_length)) { + + goto finish; + } + + if (loaded_kext_paniclist) { + kfree(loaded_kext_paniclist, loaded_kext_paniclist_size); + } + loaded_kext_paniclist = newlist; + loaded_kext_paniclist_size = newlist_size; + loaded_kext_paniclist_length = newlist_length; + +finish: + IORecursiveLockUnlock(sKextLock); + return; +} + +/********************************************************************* +*********************************************************************/ +/* static */ +void +OSKext::saveUnloadedKextPanicList(OSKext * aKext) +{ + char * newlist = NULL; + uint32_t newlist_size = 0; + uint32_t newlist_length = 0; + char identPlusVers[2*KMOD_MAX_NAME]; + uint32_t identPlusVersLength; + + if (!aKext->kmod_info) { + return; // do not goto finish here b/c of lock + } + + IORecursiveLockLock(sKextLock); + + clock_get_uptime(&last_unloaded_timestamp); + last_unloaded_address = (void *)aKext->kmod_info->address; + last_unloaded_size = aKext->kmod_info->size; + + + identPlusVersLength = assemble_identifier_and_version(aKext->kmod_info, + identPlusVers); + if (!identPlusVersLength) { + printf("error saving unloaded kext info\n"); + goto finish; + } + + newlist_length = identPlusVersLength; + newlist_size = newlist_length + 1; + newlist = (char *)kalloc(newlist_size); + + if (!newlist) { + printf("couldn't allocate kext panic log buffer\n"); + goto finish; + } + + newlist[0] = '\0'; + + strlcpy(newlist, identPlusVers, newlist_size); + + if (unloaded_kext_paniclist) { + kfree(unloaded_kext_paniclist, unloaded_kext_paniclist_size); + } + unloaded_kext_paniclist = newlist; + unloaded_kext_paniclist_size = newlist_size; + unloaded_kext_paniclist_length = newlist_length; + +finish: + IORecursiveLockUnlock(sKextLock); + return; +} + +/********************************************************************* +*********************************************************************/ +#if __LP64__ +#define __kLoadSizeEscape "0x%lld" +#else +#define __kLoadSizeEscape "0x%ld" +#endif /* __LP64__ */ + +/* static */ +void +OSKext::printKextPanicLists(int (*printf_func)(const char *fmt, ...)) +{ + printf_func("unloaded kexts:\n"); + if (unloaded_kext_paniclist && + pmap_find_phys(kernel_pmap, (addr64_t) (uintptr_t) unloaded_kext_paniclist) && + unloaded_kext_paniclist[0]) { + + printf_func( + "%.*s (addr %p, size " __kLoadSizeEscape ") - last unloaded %llu\n", + unloaded_kext_paniclist_length, unloaded_kext_paniclist, + last_unloaded_address, last_unloaded_size, + AbsoluteTime_to_scalar(&last_unloaded_timestamp)); + } else { + printf_func("(none)\n"); + } + 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", loaded_kext_paniclist_length, loaded_kext_paniclist); + } else { + printf_func("(none)\n"); + } + return; +} + +/********************************************************************* +*********************************************************************/ +#if __ppc__ || __i386__ +/* static */ +kern_return_t +OSKext::getKmodInfo( + kmod_info_array_t * kmodList, + mach_msg_type_number_t * kmodCount) +{ + kern_return_t result = KERN_FAILURE; + vm_offset_t data; + kmod_info_t * k, * kmod_info_scan_ptr; + kmod_reference_t * r, * ref_scan_ptr; + int ref_count; + unsigned size = 0; + + *kmodList = (kmod_info_t *)0; + *kmodCount = 0; + + IORecursiveLockLock(sKextLock); + + k = kmod; + while (k) { + size += sizeof(kmod_info_t); + r = k->reference_list; + while (r) { + size +=sizeof(kmod_reference_t); + r = r->next; + } + k = k->next; + } + if (!size) { + result = KERN_SUCCESS; + goto finish; + } + + result = kmem_alloc(kernel_map, &data, size); + if (result != KERN_SUCCESS) { + goto finish; + } + + /* Copy each kmod_info struct sequentially into the data buffer. + * Set each struct's nonzero 'next' pointer back to itself as a sentinel; + * the kernel space address is used to match refs, and a zero 'next' flags + * the end of kmod_infos in the data buffer and the beginning of references. + */ + k = kmod; + kmod_info_scan_ptr = (kmod_info_t *)data; + while (k) { + *kmod_info_scan_ptr = *k; + if (k->next) { + kmod_info_scan_ptr->next = k; + } + kmod_info_scan_ptr++; + k = k->next; + } + + /* Now add references after the kmod_info structs in the same buffer. + * Update each kmod_info with the ref_count so we can associate + * references with kmod_info structs. + */ + k = kmod; + ref_scan_ptr = (kmod_reference_t *)kmod_info_scan_ptr; + kmod_info_scan_ptr = (kmod_info_t *)data; + while (k) { + r = k->reference_list; + ref_count = 0; + while (r) { + /* Note the last kmod_info in the data buffer has its next == 0. + * Since there can only be one like that, + * this case is handled by the caller. + */ + *ref_scan_ptr = *r; + ref_scan_ptr++; + r = r->next; + ref_count++; + } + /* Stuff the # of refs into the 'reference_list' field of the kmod_info + * struct for the client to interpret. + */ + kmod_info_scan_ptr->reference_list = (kmod_reference_t *)(long)ref_count; + kmod_info_scan_ptr++; + k = k->next; + } + + result = vm_map_copyin(kernel_map, data, size, TRUE, (vm_map_copy_t *)kmodList); + if (result != KERN_SUCCESS) { + goto finish; + } + + *kmodCount = size; + result = KERN_SUCCESS; + +finish: + IORecursiveLockUnlock(sKextLock); + + if (result != KERN_SUCCESS && data) { + kmem_free(kernel_map, data, size); + *kmodList = (kmod_info_t *)0; + *kmodCount = 0; + } + return result; +} +#endif /* __ppc__ || __i386__ */ +#if PRAGMA_MARK +#pragma mark MAC Framework Support +#endif +/********************************************************************* +*********************************************************************/ +#if CONFIG_MACF_KEXT +/* MAC Framework support */ + +/* + * define IOC_DEBUG to display run-time debugging information + * #define IOC_DEBUG 1 + */ + +#ifdef IOC_DEBUG +#define DPRINTF(x) printf x +#else +#define IOC_DEBUG +#define DPRINTF(x) +#endif + +/********************************************************************* +*********************************************************************/ +static bool +MACFObjectIsPrimitiveType(OSObject * obj) +{ + const OSMetaClass * typeID = NULL; // do not release + + typeID = OSTypeIDInst(obj); + if (typeID == OSTypeID(OSString) || typeID == OSTypeID(OSNumber) || + typeID == OSTypeID(OSBoolean) || typeID == OSTypeID(OSData)) { + + return true; + } + return false; +} + +/********************************************************************* +*********************************************************************/ +static int +MACFLengthForObject(OSObject * obj) +{ + const OSMetaClass * typeID = NULL; // do not release + int len; + + typeID = OSTypeIDInst(obj); + if (typeID == OSTypeID(OSString)) { + OSString * stringObj = OSDynamicCast(OSString, obj); + len = stringObj->getLength() + 1; + } else if (typeID == OSTypeID(OSNumber)) { + len = sizeof("4294967295"); /* UINT32_MAX */ + } else if (typeID == OSTypeID(OSBoolean)) { + OSBoolean * boolObj = OSDynamicCast(OSBoolean, obj); + len = boolObj->isTrue() ? sizeof("true") : sizeof("false"); + } else if (typeID == OSTypeID(OSData)) { + OSData * dataObj = OSDynamicCast(OSData, obj); + len = dataObj->getLength(); + } else { + len = 0; + } + return len; +} + +/********************************************************************* +*********************************************************************/ +static void +MACFInitElementFromObject( + struct mac_module_data_element * element, + OSObject * value) +{ + const OSMetaClass * typeID = NULL; // do not release + + typeID = OSTypeIDInst(value); + if (typeID == OSTypeID(OSString)) { + OSString * stringObj = OSDynamicCast(OSString, value); + element->value_type = MAC_DATA_TYPE_PRIMITIVE; + element->value_size = stringObj->getLength() + 1; + DPRINTF(("osdict: string %s size %d\n", + stringObj->getCStringNoCopy(), element->value_size)); + memcpy(element->value, stringObj->getCStringNoCopy(), + element->value_size); + } else if (typeID == OSTypeID(OSNumber)) { + OSNumber * numberObj = OSDynamicCast(OSNumber, value); + element->value_type = MAC_DATA_TYPE_PRIMITIVE; + element->value_size = sprintf(element->value, "%u", + numberObj->unsigned32BitValue()) + 1; + } else if (typeID == OSTypeID(OSBoolean)) { + OSBoolean * boolObj = OSDynamicCast(OSBoolean, value); + element->value_type = MAC_DATA_TYPE_PRIMITIVE; + if (boolObj->isTrue()) { + strcpy(element->value, "true"); + element->value_size = 5; + } else { + strcpy(element->value, "false"); + element->value_size = 6; + } + } else if (typeID == OSTypeID(OSData)) { + OSData * dataObj = OSDynamicCast(OSData, value); + element->value_type = MAC_DATA_TYPE_PRIMITIVE; + element->value_size = dataObj->getLength(); + DPRINTF(("osdict: data size %d\n", dataObj->getLength())); + memcpy(element->value, dataObj->getBytesNoCopy(), + element->value_size); + } + return; +} + +/********************************************************************* +* This function takes an OSDictionary and returns a struct mac_module_data +* list. +*********************************************************************/ +static struct mac_module_data * +MACFEncodeOSDictionary(OSDictionary * dict) +{ + struct mac_module_data * result = NULL; // do not free + const OSMetaClass * typeID = NULL; // do not release + OSString * key = NULL; // do not release + OSCollectionIterator * keyIterator = NULL; // must release + struct mac_module_data_element * element = NULL; // do not free + unsigned int strtabsize = 0; + unsigned int listtabsize = 0; + unsigned int dicttabsize = 0; + unsigned int nkeys = 0; + unsigned int datalen = 0; + char * strtab = NULL; // do not free + char * listtab = NULL; // do not free + char * dicttab = NULL; // do not free + vm_offset_t data_addr = 0; + + keyIterator = OSCollectionIterator::withCollection(dict); + if (!keyIterator) { + goto finish; + } + + /* Iterate over OSModuleData to figure out total size */ + while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) { + + // Get the key's value and determine its type + OSObject * value = dict->getObject(key); + if (!value) { + continue; + } + + typeID = OSTypeIDInst(value); + if (MACFObjectIsPrimitiveType(value)) { + strtabsize += MACFLengthForObject(value); + } + else if (typeID == OSTypeID(OSArray)) { + unsigned int k, cnt, nents; + OSArray * arrayObj = OSDynamicCast(OSArray, value); + + nents = 0; + cnt = arrayObj->getCount(); + for (k = 0; k < cnt; k++) { + value = arrayObj->getObject(k); + typeID = OSTypeIDInst(value); + if (MACFObjectIsPrimitiveType(value)) { + listtabsize += MACFLengthForObject(value); + nents++; + } + else if (typeID == OSTypeID(OSDictionary)) { + unsigned int dents = 0; + OSDictionary * dictObj = NULL; // do not release + OSString * dictkey = NULL; // do not release + OSCollectionIterator * dictIterator = NULL; // must release + + dictObj = OSDynamicCast(OSDictionary, value); + dictIterator = OSCollectionIterator::withCollection(dictObj); + if (!dictIterator) { + goto finish; + } + while ((dictkey = OSDynamicCast(OSString, + dictIterator->getNextObject()))) { + + OSObject * dictvalue = NULL; // do not release + + dictvalue = dictObj->getObject(dictkey); + if (!dictvalue) { + continue; + } + if (MACFObjectIsPrimitiveType(dictvalue)) { + strtabsize += MACFLengthForObject(dictvalue); + } else { + continue; /* Only handle primitive types here. */ + } + /* + * Allow for the "arraynnn/" prefix in the key length. + */ + strtabsize += dictkey->getLength() + 1; + dents++; + } + dictIterator->release(); + if (dents-- > 0) { + dicttabsize += sizeof(struct mac_module_data_list) + + dents * sizeof(struct mac_module_data_element); + nents++; + } + } + else { + continue; /* Skip everything else. */ + } + } + if (nents == 0) { + continue; + } + listtabsize += sizeof(struct mac_module_data_list) + + (nents - 1) * sizeof(struct mac_module_data_element); + } else { + continue; /* skip anything else */ + } + strtabsize += key->getLength() + 1; + nkeys++; + } + if (nkeys == 0) { + goto finish; + } + + /* + * Allocate and fill in the module data structures. + */ + datalen = sizeof(struct mac_module_data) + + sizeof(mac_module_data_element) * (nkeys - 1) + + strtabsize + listtabsize + dicttabsize; + DPRINTF(("osdict: datalen %d strtabsize %d listtabsize %d dicttabsize %d\n", + datalen, strtabsize, listtabsize, dicttabsize)); + if (kmem_alloc(kernel_map, &data_addr, datalen) != KERN_SUCCESS) { + goto finish; + } + result = (mac_module_data *)data_addr; + result->base_addr = data_addr; + result->size = datalen; + result->count = nkeys; + strtab = (char *)&result->data[nkeys]; + listtab = strtab + strtabsize; + dicttab = listtab + listtabsize; + DPRINTF(("osdict: data_addr %p strtab %p listtab %p dicttab %p end %p\n", + data_addr, strtab, listtab, dicttab, data_addr + datalen)); + + keyIterator->reset(); + nkeys = 0; + element = &result->data[0]; + DPRINTF(("osdict: element %p\n", element)); + while ( (key = OSDynamicCast(OSString, keyIterator->getNextObject())) ) { + + // Get the key's value and determine its type + OSObject * value = dict->getObject(key); + if (!value) { + continue; + } + + /* Store key */ + DPRINTF(("osdict: element @%p\n", element)); + element->key = strtab; + element->key_size = key->getLength() + 1; + DPRINTF(("osdict: key %s size %d @%p\n", key->getCStringNoCopy(), + element->key_size, strtab)); + memcpy(element->key, key->getCStringNoCopy(), element->key_size); + + typeID = OSTypeIDInst(value); + if (MACFObjectIsPrimitiveType(value)) { + /* Store value */ + element->value = element->key + element->key_size; + DPRINTF(("osdict: primitive element value %p\n", element->value)); + MACFInitElementFromObject(element, value); + strtab += element->key_size + element->value_size; + DPRINTF(("osdict: new strtab %p\n", strtab)); + } else if (typeID == OSTypeID(OSArray)) { + unsigned int k, cnt, nents; + char *astrtab; + struct mac_module_data_list *arrayhd; + struct mac_module_data_element *ele; + OSArray *arrayObj = OSDynamicCast(OSArray, value); + + element->value = listtab; + DPRINTF(("osdict: array element value %p\n", element->value)); + element->value_type = MAC_DATA_TYPE_ARRAY; + arrayhd = (struct mac_module_data_list *)element->value; + arrayhd->type = 0; + DPRINTF(("osdict: arrayhd %p\n", arrayhd)); + nents = 0; + astrtab = strtab + element->key_size; + ele = &(arrayhd->list[0]); + cnt = arrayObj->getCount(); + for (k = 0; k < cnt; k++) { + value = arrayObj->getObject(k); + DPRINTF(("osdict: array ele %d @%p\n", nents, ele)); + ele->key = NULL; + ele->key_size = 0; + typeID = OSTypeIDInst(value); + if (MACFObjectIsPrimitiveType(value)) { + if (arrayhd->type != 0 && + arrayhd->type != MAC_DATA_TYPE_PRIMITIVE) { + + continue; + } + arrayhd->type = MAC_DATA_TYPE_PRIMITIVE; + ele->value = astrtab; + MACFInitElementFromObject(ele, value); + astrtab += ele->value_size; + DPRINTF(("osdict: array new astrtab %p\n", astrtab)); + } else if (typeID == OSTypeID(OSDictionary)) { + unsigned int dents; + char * dstrtab = NULL; // do not free + OSDictionary * dictObj = NULL; // do not release + OSString * dictkey = NULL; // do not release + OSCollectionIterator * dictIterator = NULL; // must release + struct mac_module_data_list * dicthd = NULL; // do not free + struct mac_module_data_element * dele = NULL; // do not free + + if (arrayhd->type != 0 && + arrayhd->type != MAC_DATA_TYPE_DICT) { + + continue; + } + dictObj = OSDynamicCast(OSDictionary, value); + dictIterator = OSCollectionIterator::withCollection(dictObj); + if (!dictIterator) { + goto finish; + } + DPRINTF(("osdict: dict\n")); + ele->value = dicttab; + ele->value_type = MAC_DATA_TYPE_DICT; + dicthd = (struct mac_module_data_list *)ele->value; + DPRINTF(("osdict: dicthd %p\n", dicthd)); + dstrtab = astrtab; + dents = 0; + while ((dictkey = OSDynamicCast(OSString, + dictIterator->getNextObject()))) { + + OSObject * dictvalue = NULL; // do not release + + dictvalue = dictObj->getObject(dictkey); + if (!dictvalue) { + continue; + } + dele = &(dicthd->list[dents]); + DPRINTF(("osdict: dict ele %d @%p\n", dents, dele)); + if (MACFObjectIsPrimitiveType(dictvalue)) { + dele->key = dstrtab; + dele->key_size = dictkey->getLength() + 1; + DPRINTF(("osdict: dictkey %s size %d @%p\n", + dictkey->getCStringNoCopy(), dictkey->getLength(), dstrtab)); + memcpy(dele->key, dictkey->getCStringNoCopy(), + dele->key_size); + dele->value = dele->key + dele->key_size; + MACFInitElementFromObject(dele, dictvalue); + dstrtab += dele->key_size + dele->value_size; + DPRINTF(("osdict: dict new dstrtab %p\n", dstrtab)); + } else { + continue; /* Only handle primitive types here. */ + } + dents++; + } + dictIterator->release(); + if (dents == 0) { + continue; + } + arrayhd->type = MAC_DATA_TYPE_DICT; + ele->value_size = sizeof(struct mac_module_data_list) + + (dents - 1) * sizeof(struct mac_module_data_element); + DPRINTF(("osdict: dict ele size %d ents %d\n", ele->value_size, dents)); + dicttab += ele->value_size; + DPRINTF(("osdict: new dicttab %p\n", dicttab)); + dicthd->count = dents; + astrtab = dstrtab; + } else { + continue; /* Skip everything else. */ + } + nents++; + ele++; + } + if (nents == 0) { + continue; + } + element->value_size = sizeof(struct mac_module_data_list) + + (nents - 1) * sizeof(struct mac_module_data_element); + listtab += element->value_size; + DPRINTF(("osdict: new listtab %p\n", listtab)); + arrayhd->count = nents; + strtab = astrtab; + DPRINTF(("osdict: new strtab %p\n", strtab)); + } else { + continue; /* skip anything else */ + } + element++; + } + DPRINTF(("result list @%p, key %p value %p\n", + result, result->data[0].key, result->data[0].value)); +finish: + if (keyIterator) keyIterator->release(); + return result; +} + +/********************************************************************* +* This function takes a plist and looks for an OSModuleData dictionary. +* If it is found, an encoded copy is returned. The value must be +* kmem_free()'d. +*********************************************************************/ +static void * +MACFCopyModuleDataForKext( + OSKext * theKext, + mach_msg_type_number_t * datalen) + +{ + struct mac_module_data * result = NULL; + OSDictionary * kextModuleData = NULL; // do not release + vm_map_copy_t copy = 0; + + kextModuleData = OSDynamicCast(OSDictionary, + theKext->getPropertyForHostArch("OSModuleData")); + if (!kextModuleData) { + goto finish; + } + + result = MACFEncodeOSDictionary(kextModuleData); + if (!result) { + goto finish; + } + *datalen = module_data->size; + +finish: + return (void *)result; +} +#endif /* CONFIG_MACF_KEXT */ diff -Naur xnu-1486.2.11.orig/osfmk/conf/files.i386 xnu-1486.2.11/osfmk/conf/files.i386 --- xnu-1486.2.11.orig/osfmk/conf/files.i386 2009-11-12 13:00:03.000000000 -0500 +++ xnu-1486.2.11/osfmk/conf/files.i386 2009-11-16 22:57:59.000000000 -0500 @@ -84,6 +84,7 @@ osfmk/i386/commpage/bcopy_scalar.s standard osfmk/i386/commpage/bcopy_sse2.s standard osfmk/i386/commpage/bcopy_sse3x.s standard +osfmk/i386/commpage/bcopy_sse3_64.s standard osfmk/i386/commpage/bcopy_sse3x_64.s standard osfmk/i386/commpage/bcopy_sse42.s standard osfmk/i386/commpage/bcopy_sse42_64.s standard @@ -97,6 +98,7 @@ osfmk/i386/commpage/longcopy_sse3x.s standard osfmk/i386/commpage/longcopy_sse3x_64.s standard osfmk/i386/commpage/commpage_sigs.c standard +osfmk/i386/commpage/sse3emu.c standard osfmk/i386/commpage/fifo_queues.s standard osfmk/i386/AT386/conf.c standard diff -Naur xnu-1486.2.11.orig/osfmk/i386/.cpuid.c.swp xnu-1486.2.11/osfmk/i386/.cpuid.c.swp --- xnu-1486.2.11.orig/osfmk/i386/.cpuid.c.swp 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/.cpuid.c.swp 2009-11-17 00:19:42.000000000 -0500 @@ -0,0 +1,123 @@ +b0VIM 7.2Õ1K¯#4nawcomphatphuck.local~nawcom/Builds/xnu-backup/osfmk/i386/cpuid.cutf-8 U3210#"! Utp ÿl ]mšÊ +d jg uЃEyÈ~A¢¿aadÉlý¸µ‰…;ò®bÒ ‰ G C  Ì È € 7 ð ¬ b  ÷ +ó +É +Å + +¯ +« +” +~ +f +I +) + +÷ ê Ê ± – ~ ` D )   ß«ªŽp.ôóðÊÆ·zi;ò×µr[8öѳ”u3òÔ¶utL$õÁ™g, À¼®œ™xt+ðÉÈ///// value type level ways size entries// -------------------------------------------------------static cpuid_cache_descriptor_t intel_cpuid_leaf2_descriptor_table[] = { */ * Intel cache descriptor table:/*#define M (1024)#define K (1) */ * These multipliers are used to encode 1*K .. 64*M in a 16 bit size field /*} cpuid_cache_descriptor_t; uint16_t entries; /* number of TLB entries or linesize */ uint16_t size; /* cachesize or TLB pagesize */ uint8_t ways; /* wayness of cache */ uint8_t level; /* level of cache/TLB hierachy */ uint8_t type; /* cpuid_leaf2_desc_type_t */ uint8_t value; /* descriptor code */typedef struct cpuid_cache_descriptor {} cpuid_leaf2_qualifier_t; BOTH /* Small and Large page TLB */ LARGE, /* Large page TLB */ SMALL, /* Small page TLB */ L3_2LINESECTOR, /* L3(unified) cache with 2 lines per sector */ L2_2LINESECTOR, /* L2 (unified) cache with 2 lines per sector */ L3, /* L3 (unified) cache */ L2, /* L2 (unified) cache */ L1_DATA, /* L1 Data cache */ L1_INST, /* L1 Instruction cache */ L1, /* L1 (unified) cache */ DATA1, /* Data TLB, 2nd level */ DATA0, /* Data TLB, 1st level */ DATA, /* Data TLB */ INST, /* Instruction TLB */ TRACE, /* Trace Cache (P4 only) */ FULLY, /* Fully-associative */ NA, /* Not Applicable */typedef enum {} cpuid_leaf2_desc_type_t; PREFETCH /* Prefetch size */ STLB, /* Shared second-level unified TLB */ TLB, /* TLB */ CACHE, /* Cache */ _NULL_, /* NULL (empty) descriptor */typedef enum { */ * Leaf 2 cache descriptor encodings./*#define bitfield32(x,h,l) ((((x) & bitmask32(h,l)) >> l))#define bitmask32(h,l) ((bit32(h)|(bit32(h)-1)) & ~(bit32(l)-1))#define bit32(n) (1U << (n))/* Only for 32bit values */#define quad(hi,lo) (((uint64_t)(hi)) << 32 | (lo))#define min(a,b) ((a) < (b) ? (a) : (b))#endif#include #include #include #include #include #include #include #include #if MACH_KDB#include "cpuid_legacy.h"#include #include #include #include #include #include */ * @OSF_COPYRIGHT@/* */ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ * * limitations under the License. * Please see the License for the specific language governing rights and * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * The Original Code and all software distributed under the License are * * http://www.opensource.apple.com/apsl/ and read it before using this file. * Please obtain a copy of the License at * * terms of an Apple operating system software license agreement. * circumvent, violate, or enable the circumvention or violation of, any * unlawful or unlicensed copies of an Apple operating system, or to * may not be used to create, or enable the creation or redistribution of, * compliance with the License. The rights granted to you under the License * Version 2.0 (the 'License'). You may not use this file except in * as defined in and that are subject to the Apple Public Source License * This file contains Original Code and/or Modifications of Original Code * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved./*ad j ϧ{HE32ïÆ›`_8þ Ö ª w t r q j i #endif} } i, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]); db_printf("%08X - %08X %08X %08X %08X\n", do_cpuid(i, cpid); /* Get the next */ for (i = 0x80000001; i <= mid; i++) { /* Dump 'em out */ mid = cpid[eax]; /* Set the number */ 0x80000000, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]); db_printf("%08X - %08X %08X %08X %08X\n", * is the number of extended ids */ do_cpuid(0x80000000, cpid); /* Get the first extended cpuid which db_printf("\n"); } i, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]); db_printf("%08X - %08X %08X %08X %08X\n", do_cpuid(i, cpid); /* Get the next */ for (i = 1; i <= mid; i++) { /* Dump 'em out */adšÚ´f>Æ ŸvVTCBÚ ¨ š ˜ — ” t p o ?   ÿ Å Ã ¦ ¤ ž d b G +  ý æ Ð » ± “ Ž ‡ h g 1  û +ù +â +Ê +¦ +‚ +` +Q +K +; +8 +7 + +û ù ø Æ Á ­ ‘ w ` A + )  ñؾŸ‰sWA/ +þêÌ·­¡oZPD0ýóçÓµ –ˆ}rnl1 +öôÁ¿§{[8ëá kd:$Üů¥›X5èÞÓ´ƒ€ }; return; /* not reached, silences optimizer */ BUG("invalid cache level"); default: break; cache_sharing = ncores; cache_size = cache_size * 512 * 1024 / ncores; * we export it as per-core. */ /* L3 is in 512 KB units. This is reported by CPU as total, and case 3: break; cache_sharing = 1; cache_size *= 1024; /* L2 cache is in KB units, reported per-core */ case 2: break; cache_sharing = 1; cache_size = (cache_size >> 8) * 1024; */ * This is in KB units, and is reported per-core. /* L1 uses only bits 31 to 24 so we should shift right 8 bits case 1: switch (cache_level) { } return; info_p->cache_partitions[type] = 0; info_p->cache_sharing[type] = 0; info_p->cache_size[type] = 0; /* Cache doesn't exist, set it as zero */ if (cache_size == 0) { cache_size = bitfield32(reg[reg_to_use], 31, 16); ncores = n_cores; else ncores = 1; if (n_cores == 0) /* Avoid div0 errors if we couldn't get the # of cores */ }; return; default: case Lnone: break; reg_to_use = edx; do_cpuid(0x80000006, reg); cache_level = 3; case L3U: break; reg_to_use = ecx; do_cpuid(0x80000006, reg); cache_level = 2; case L2U: break; reg_to_use = edx; do_cpuid(0x80000005, reg); cache_level = 1; case L1I: break; reg_to_use = ecx; do_cpuid(0x80000005, reg); cache_level = 1; case L1D: switch (type) { uint32_t ncores; uint32_t colors; uint32_t reg_to_use; uint32_t cache_partitions; uint32_t cache_sets; uint32_t cache_size; uint32_t cache_associativity; uint32_t cache_linesize; uint32_t cache_sharing; uint32_t cache_level; uint32_t reg[4] = {0, 0, 0, 0};{ uint32_t n_cores) uint32_t* geometry_colors, cache_type_t type, uint32_t linesizes[], i386_cpu_info_t* info_p,get_amd_cache_info(void/* mercurysquad: this function is AMD-specific */} return !IsAmdCPU(); // dirty hackboolean_t IsIntelCPU() {}; return FALSE; else return TRUE; ourcpuid[edx] == 0x69746E65) ourcpuid[ecx] == 0x444D4163 && if (ourcpuid[ebx] == 0x68747541 && do_cpuid(0, ourcpuid); uint32_t ourcpuid[4]; if (ForceAmdCpu) return TRUE;boolean_t IsAmdCPU() {/* Handy functions to check what platform we're on */boolean_t ForceAmdCpu = FALSE;#endif} } do_cpuid(selector, result); } else { : "a"(selector)); "=d" (result[3]) "=c" (result[2]), "=b" (result[1]), : "=a" (result[0]), asm("call _cpuid64" if (cpu_mode_is64bit()) {{static void cpuid_fn(uint32_t selector, uint32_t *result)#else} do_cpuid(selector, result);{static void cpuid_fn(uint32_t selector, uint32_t *result)#if defined(__x86_64__)static i386_cpu_info_t cpuid_cpu_info;static i386_cpu_info_t *cpuid_cpu_infop = NULL; */ * CPU identification routines./*} return NULL; return &intel_cpuid_leaf2_descriptor_table[i]; if (intel_cpuid_leaf2_descriptor_table[i].value == value) for (i = 0; i < INTEL_LEAF2_DESC_NUM; i++) unsigned int i;{cpuid_leaf2_find(uint8_t value)static inline cpuid_cache_descriptor_t * sizeof(cpuid_cache_descriptor_t))#define INTEL_LEAF2_DESC_NUM (sizeof(intel_cpuid_leaf2_descriptor_table) / \}; { 0xF1, PREFETCH, NA, NA, 128, NA } { 0xF0, PREFETCH, NA, NA, 64, NA }, { 0xE6, CACHE, L3, 16, 24*M, 64 }, { 0xE5, CACHE, L3, 16, 16*M, 64 }, { 0xE4, CACHE, L3, 16, 8*M, 64 }, { 0xE3, CACHE, L3, 16, 4*M, 64 }, ad~öòéæå¿¬ª§¢‹^I÷ö¤£{GFð ¢ u . å ” … \ Y X T # æ á ½ œ k / ó +ê +æ +ã +à +‰ +M +J +I + + + +ü ì Ù Å ¥ … f F &  æÆ¦…eD$ãá_@ÿÞ¾~^?üÙ¸—vU2ïάˆdAüôñÜ´‘kFúòïîÜËÉœ|hC@'%$char *} return cpuid_cpu_infop; } cpuid_cpu_infop = &cpuid_cpu_info; cpuid_set_info(); if (cpuid_cpu_infop == NULL) { /* Set-up the cpuid_info stucture lazily */{cpuid_info(void)i386_cpu_info_t *}; {0, 0} {CPUID_EXTFEATURE_TSCI, "TSCI"}, {CPUID_EXTFEATURE_RDTSCP, "RDTSCP"}, {CPUID_EXTFEATURE_LAHF, "LAHF"}, {CPUID_EXTFEATURE_EM64T, "EM64T"}, {CPUID_EXTFEATURE_XD, "XD"}, {CPUID_EXTFEATURE_SYSCALL, "SYSCALL"},extfeature_map[] = {}, {0, 0} {CPUID_FEATURE_VMM, "VMM"}, {CPUID_FEATURE_POPCNT, "POPCNT"}, {CPUID_FEATURE_xAPIC, "xAPIC"}, {CPUID_FEATURE_SSE4_2, "SSE4.2"}, {CPUID_FEATURE_SSE4_1, "SSE4.1"}, {CPUID_FEATURE_PDCM, "PDCM"}, {CPUID_FEATURE_xTPR, "TPR"}, {CPUID_FEATURE_CX16, "CX16"}, {CPUID_FEATURE_CID, "CID"}, {CPUID_FEATURE_SSSE3, "SSSE3"}, {CPUID_FEATURE_TM2, "TM2"}, {CPUID_FEATURE_EST, "EST"}, {CPUID_FEATURE_SMX, "SMX"}, {CPUID_FEATURE_VMX, "VMX"}, {CPUID_FEATURE_DSCPL, "DSCPL"}, {CPUID_FEATURE_MONITOR, "MON"}, {CPUID_FEATURE_SSE3, "SSE3"}, {CPUID_FEATURE_TM, "TM",}, {CPUID_FEATURE_HTT, "HTT",}, {CPUID_FEATURE_SS, "SS",}, {CPUID_FEATURE_SSE2, "SSE2",}, {CPUID_FEATURE_SSE, "SSE",}, {CPUID_FEATURE_FXSR, "FXSR",}, {CPUID_FEATURE_MMX, "MMX",}, {CPUID_FEATURE_ACPI, "ACPI",}, {CPUID_FEATURE_DS, "DS",}, {CPUID_FEATURE_CLFSH, "CLFSH",}, {CPUID_FEATURE_PSN, "PSN",}, {CPUID_FEATURE_PSE36, "PSE36",}, {CPUID_FEATURE_PAT, "PAT",}, {CPUID_FEATURE_CMOV, "CMOV",}, {CPUID_FEATURE_MCA, "MCA",}, {CPUID_FEATURE_PGE, "PGE",}, {CPUID_FEATURE_MTRR, "MTRR",}, {CPUID_FEATURE_SEP, "SEP",}, {CPUID_FEATURE_APIC, "APIC",}, {CPUID_FEATURE_CX8, "CX8",}, {CPUID_FEATURE_MCE, "MCE",}, {CPUID_FEATURE_PAE, "PAE",}, {CPUID_FEATURE_MSR, "MSR",}, {CPUID_FEATURE_TSC, "TSC",}, {CPUID_FEATURE_PSE, "PSE",}, {CPUID_FEATURE_DE, "DE",}, {CPUID_FEATURE_VME, "VME",}, {CPUID_FEATURE_FPU, "FPU",},} feature_map[] = { const char *name; uint64_t mask;static struct {} cpuid_cpu_info.cpuid_model_string = ""; /* deprecated */ } info_p->thread_count = info_p->cpuid_logical_per_package; info_p->core_count = info_p->cpuid_cores_per_package; if (info_p->core_count == 0) { } } break; info_p->thread_count = bitfield32((uint32_t)msr, 15, 0); info_p->core_count = bitfield32((uint32_t)msr, 31, 16); uint64_t msr = rdmsr64(MSR_CORE_THREAD_COUNT); case CPUFAMILY_INTEL_NEHALEM: { switch (info_p->cpuid_cpufamily) { */ * (which determines whether SMT/Hyperthreading is active). * Find the number of enabled cores and threads /* cpuid_set_cache_info(&cpuid_cpu_info); } else bcopy(CPUID_VID_INTEL, cpuid_cpu_info.cpuid_vendor, sizeof(CPUID_VID_INTEL)); if (PE_parse_boot_argn("-emulateintel", &dummyVar, sizeof (dummyVar))) /* The following is a non-ideal solution but seems to be required */ cpuid_set_amd_cache_info(&cpuid_cpu_info); ForceAmdCpu = TRUE; // force from now so we dont have to do cpuid each time if (IsAmdCPU() || PE_parse_boot_argn("-amd", &dummyVar, sizeof (dummyVar))) { info_p->cpuid_cpu_subtype = CPU_SUBTYPE_X86_ARCH1; info_p->cpuid_cpu_type = CPU_TYPE_X86;/* mercurysquad: removed the supported CPU check, and add routing for AMD cpus */ cpuid_set_generic_info(info_p); bzero((void *)info_p, sizeof(cpuid_cpu_info)); uint32_t dummyVar; i386_cpu_info_t *info_p = &cpuid_cpu_info;{cpuid_set_info(void)void*/} return cpufamily; info_p->cpuid_cpufamily = cpufamily; } break; } break;ad1Í¢¸¶¤”Œ‹]0#¹©Ÿymj^QONGù ÷ å Õ Í Ì › h [ L = ð à Ö ­ ¡ ž ’ … ƒ ‚ | e P N > = ò Ø «  U * ÿ +Ç +š +_ +\ +Z +Y +T +: +% +# + + +õ Á § ¥ ¤ Ÿ Œ w u = üöôóæÓÑ­«ª¡‹‰b`_T@>ðîÅù¤¢‰nmWFÒ›=¶|b% +àÞÝÔ¼º‘Œ~{f_YUO-÷ßÝÜé¨fR'õôÍÌ mid = cpid[eax]; /* Set the number */ 0, cpid[eax], cpid[ebx], cpid[ecx], cpid[edx]); db_printf("%08X - %08X %08X %08X %08X\n", * basic ids */ do_cpuid(0, cpid); /* Get the first cpuid which is the number of uint32_t cpid[4]; uint32_t i, mid;{ __unused char *modif) __unused db_expr_t count, __unused int have_addr,db_cpuid(__unused db_expr_t addr,void */ * cp * * * Display the cpuid/*#if MACH_KDB } return cpuid_info()->cpuid_extfeatures;{cpuid_extfeatures(void)uint64_t} return cpuid_cpu_info.cpuid_features; } checked = 1; } } cpuid_cpu_info.cpuid_features &= ~(CPUID_FEATURE_SSE2); printf("no sse2\n"); } else if (!strncmp("sse", fpu_arg, sizeof("sse"))) { cpuid_cpu_info.cpuid_features &= ~(CPUID_FEATURE_SSE | CPUID_FEATURE_SSE2 | CPUID_FEATURE_FXSR); printf("no sse or sse2\n"); if (!strncmp("387", fpu_arg, sizeof("387")) || !strncmp("mmx", fpu_arg, sizeof("mmx"))) { printf("limiting fpu features to: %s\n", fpu_arg); if (PE_parse_boot_argn("_fpu", &fpu_arg[0], sizeof (fpu_arg))) { /* check for boot-time fpu limitations */ if (!checked) { (void) cpuid_info(); char fpu_arg[20] = { 0 }; static int checked = 0;{cpuid_features(void)uint64_t} return cpuid_info()->cpuid_cpu_subtype;{cpuid_cpusubtype(void)cpu_subtype_t} return cpuid_info()->cpuid_cpu_type;{cpuid_cputype(void)cpu_type_t} return cpuid_info()->cpuid_cpufamily;{cpuid_cpufamily(void)uint32_t} return cpuid_info()->cpuid_family;{cpuid_family(void)unsigned int} } kprintf("%s: %s\n", header, cpuid_cpu_info.cpuid_brand_string); if (cpuid_cpu_info.cpuid_brand_string[0] != '\0') {{ const char *header)cpuid_cpu_display(void} buf, sizeof(buf))); cpuid_get_extfeature_names(cpuid_extfeatures(), kprintf("%s: %s\n", header, char buf[256];{ const char *header)cpuid_extfeature_display(void} } s_if_plural(cpuid_cpu_info.cpuid_logical_per_package)); cpuid_cpu_info.cpuid_logical_per_package, s_if_plural(cpuid_cpu_info.cpuid_cores_per_package), cpuid_cpu_info.cpuid_cores_per_package, " %d logical cpu%s per package\n", kprintf(" HTT: %d core%s per package;"#define s_if_plural(n) ((n > 1) ? "s" : "") if (cpuid_features() & CPUID_FEATURE_HTT) { buf, sizeof(buf))); cpuid_get_feature_names(cpuid_features(), kprintf("%s: %s\n", header, char buf[256];{ const char *header)cpuid_feature_display(void} return buf; *p = '\0'; } p += len; bcopy(extfeature_map[i].name, p, len); break; if (len == 0) len = min(strlen(extfeature_map[i].name), (size_t) ((buf_len-1)-(p-buf))); *p++ = ' '; if (len > 0) continue; if ((extfeatures & extfeature_map[i].mask) == 0) for (i = 0; extfeature_map[i].mask != 0; i++) { int i; char *p = buf; size_t len = -1;{cpuid_get_extfeature_names(uint64_t extfeatures, char *buf, unsigned buf_len)char *} return buf; *p = '\0'; } p += len; bcopy(feature_map[i].name, p, len); break; if (len == 0) len = min(strlen(feature_map[i].name), (size_t) ((buf_len-1) - (p-buf))); *p++ = ' '; if (len > 0) continue; if ((features & feature_map[i].mask) == 0) for (i = 0; feature_map[i].mask != 0; i++) { int i; char *p = buf; size_t len = -1;{cpuid_get_feature_names(uint64_t features, char *buf, unsigned buf_len)adùy㻘qNKJ  è¯v=Ë ’ Y $ # ú × ¤ a ) ( ÷ Ñ ¯ © €  S 6  ú +÷ +ö +½ +‘ +r +S +# + + +Ö ¬ i h A +  ÚÔÀ €`@ò·¶±ŠvF Ù§‚~WáÛÅ‘^,öסkC@?641!ôòÉȧž}rMC8ý×Í›‘uZ@!ù cpufamily = CPUFAMILY_INTEL_NEHALEM; case CPUID_MODEL_NEHALEM_EX: case CPUID_MODEL_DALES: case CPUID_MODEL_FIELDS: case CPUID_MODEL_NEHALEM: break; cpufamily = CPUFAMILY_INTEL_PENRYN; case 23: break; cpufamily = CPUFAMILY_INTEL_MEROM; case 15: break; cpufamily = CPUFAMILY_INTEL_YONAH; case 14: break; cpufamily = CPUFAMILY_INTEL_6_13; case 13: switch (info_p->cpuid_model) { case 6: switch (info_p->cpuid_family) { uint32_t cpufamily = CPUFAMILY_UNKNOWN;{cpuid_set_cpufamily(i386_cpu_info_t *info_p)static uint32_t/*} return; } info_p->cpuid_arch_perf_leafp = capp; capp->fixed_width = bitfield32(reg[edx], 12, 5); capp->fixed_number = bitfield32(reg[edx], 4, 0); capp->events = reg[ebx]; capp->events_number = bitfield32(reg[eax], 31, 24); capp->width = bitfield32(reg[eax], 23, 16); capp->number = bitfield32(reg[eax], 15, 8); capp->version = bitfield32(reg[eax], 7, 0); cpuid_fn(0xa, reg); */ * Architectural Performance Monitoring Leaf: /* cpuid_arch_perf_leaf_t *capp = &info_p->cpuid_arch_perf_leaf; if (info_p->cpuid_max_basic >= 0xa) { } info_p->cpuid_thermal_leafp = ctp; ctp->ACNT_MCNT = bitfield32(reg[ecx], 0, 0); ctp->thresholds = bitfield32(reg[ebx], 3, 0); ctp->dynamic_acceleration = bitfield32(reg[eax], 1, 1); ctp->sensor = bitfield32(reg[eax], 0, 0); cpuid_fn(6, reg); */ * The thermal and Power Leaf: /* cpuid_thermal_leaf_t *ctp = &info_p->cpuid_thermal_leaf; if (info_p->cpuid_max_basic >= 0x6) { } info_p->cpuid_mwait_leafp = cmp; cmp->sub_Cstates = reg[edx]; cmp->extensions = reg[ecx]; cmp->linesize_max = reg[ebx]; cmp->linesize_min = reg[eax]; cpuid_fn(5, reg); */ * Extract the Monitor/Mwait Leaf info: /* cpuid_mwait_leaf_t *cmp = &info_p->cpuid_mwait_leaf; if (info_p->cpuid_max_basic >= 0x5) { (uint32_t) (rdmsr64(MSR_IA32_BIOS_SIGN_ID) >> 32); info_p->cpuid_microcode_version = /* Find the microcode version number a.k.a. signature a.k.a. BIOS ID */ } reg[edx] & (uint32_t)CPUID_EXTFEATURE_TSCI; info_p->cpuid_extfeatures |= cpuid_fn(0x80000007, reg); if (info_p->cpuid_max_ext >= 0x80000007) { /* Fold in the Invariant TSC feature bit, if present */ } quad(reg[ecx], reg[edx]); info_p->cpuid_extfeatures = cpuid_fn(0x80000001, reg); if (info_p->cpuid_max_ext >= 0x80000001) { info_p->cpuid_logical_per_package = 1; else bitfield32(reg[ebx], 23, 16); info_p->cpuid_logical_per_package = if (info_p->cpuid_features & CPUID_FEATURE_HTT) info_p->cpuid_model += (info_p->cpuid_extmodel << 4); if (info_p->cpuid_family == 0x0f || info_p->cpuid_family == 0x06) info_p->cpuid_family += info_p->cpuid_extfamily; if (info_p->cpuid_family == 0x0f) /* Fold extensions into family/model */ info_p->cpuid_features = quad(reg[ecx], reg[edx]); info_p->cpuid_brand = bitfield32(reg[ebx], 7, 0); info_p->cpuid_extfamily = bitfield32(reg[eax], 27, 20); info_p->cpuid_extmodel = bitfield32(reg[eax], 19, 16); info_p->cpuid_type = bitfield32(reg[eax], 13, 12); info_p->cpuid_family = bitfield32(reg[eax], 11, 8); info_p->cpuid_model = bitfield32(reg[eax], 7, 4); info_p->cpuid_stepping = bitfield32(reg[eax], 3, 0); info_p->cpuid_signature = reg[eax]; cpuid_fn(1, reg); /* get processor signature and decode */ } bitfield32(reg[eax],15, 8); info_p->cpuid_address_bits_virtual = bitfield32(reg[eax], 7, 0); info_p->cpuid_address_bits_physical = cpuid_fn(0x80000008, reg);ad=ƒÆwU8ßÛ¯ˆ„cb%Þ Ý ¬ § ¤ ¢ ž ` N I 2  í  ¦ ¥ ¡ ` [ '  ÷ ç Ø × ¤ ‚ h \ "  ì +Þ +Ë +À +² +£ +” + +v +j +\ +W += +" + + +ù í Þ Ù ¡ — Š b ^ [ Y X L    âáÁ®‰Hع¸–zWV'ûöª¤‡dG#⿦¢|QPѦ8âÊ~{vP$ÈŸ|=< info_p->cpuid_cache_size = bitfield32(reg[ecx],31,16); bitfield32(reg[ecx],15,12); info_p->cpuid_cache_L2_associativity = info_p->cpuid_cache_linesize = bitfield32(reg[ecx], 7, 0); cpuid_fn(0x80000006, reg); if (info_p->cpuid_max_ext >= 0x80000006) { /* Get cache and addressing info. */ } } info_p->cpuid_brand_string[0] = '\0'; */ * and the firmware couldn't figure out what sort of CPU we have. * This string means we have a firmware-programmable brand string, /* strlen(CPUID_STRING_UNKNOWN) + 1))) { min(sizeof(info_p->cpuid_brand_string), if (!strncmp(info_p->cpuid_brand_string, CPUID_STRING_UNKNOWN, p, sizeof(info_p->cpuid_brand_string)); strlcpy(info_p->cpuid_brand_string, } if (*p != ' ') break; for (p = str; *p != '\0'; p++) { bcopy((char *)reg, &str[32], 16); cpuid_fn(0x80000004, reg); bcopy((char *)reg, &str[16], 16); cpuid_fn(0x80000003, reg); bcopy((char *)reg, &str[0], 16); cpuid_fn(0x80000002, reg); */ * be NUL terminated. * The brand string 48 bytes (max), guaranteed to /* if (info_p->cpuid_max_ext >= 0x80000004) { /* check to see if we can get brand string */ info_p->cpuid_max_ext = reg[eax]; cpuid_fn(0x80000000, reg); /* get extended cpuid results */ info_p->cpuid_vendor[12] = 0; bcopy((char *)®[edx], &info_p->cpuid_vendor[4], 4); bcopy((char *)®[ecx], &info_p->cpuid_vendor[8], 4); bcopy((char *)®[ebx], &info_p->cpuid_vendor[0], 4); /* ug */ info_p->cpuid_max_basic = reg[eax]; cpuid_fn(0, reg); /* do cpuid 0 to get vendor */ char str[128], *p; uint32_t reg[4];{cpuid_set_generic_info(i386_cpu_info_t *info_p)static void} } } info_p->cpuid_stlb = descp->entries; case STLB: break; info_p->cpuid_tlb[id][page][level] = descp->entries; } level = 0; default: break; level = 1; case DATA1: switch (descp->level) { /* determine level: */ } continue; default: break; id = TLB_DATA; case DATA1: case DATA0: case DATA: break; id = TLB_INST; case INST: switch (descp->level) { /* determine I or D: */ page = (descp->size == SMALL) ? TLB_SMALL : TLB_LARGE; case TLB: switch (descp->type) { continue; if (descp == NULL) descp = cpuid_leaf2_find(info_p->cache_info[i]); int page; int level; int id; cpuid_cache_descriptor_t *descp; for (i = 1; i < sizeof(info_p->cache_info); i++) { */ * Extract and publish TLB information from Leaf 2 descriptors. /* else panic("no linesize"); info_p->cache_linesize = linesizes[L1D]; else if (linesizes[L1D]) info_p->cache_linesize = linesizes[L2U]; if ( linesizes[L2U] ) */ * else the L1D. * What linesize to publish? We use the L2 linesize if any, /* } */ linesizes[L2U] = info_p->cpuid_cache_linesize; info_p->cache_partitions[L2U] = 1; info_p->cache_sharing[L2U] = 1; info_p->cache_size[L2U] = info_p->cpuid_cache_size * 1024; /* Apple's "something else" -- } vm_cache_geometry_colors = colors; if ( colors > vm_cache_geometry_colors ) colors = ( foundInfo.linesize * cache_sets ) >> 12; foundInfo.partitions); foundInfo.linesize * (foundInfo.associativity * cache_sets = foundInfo.totalsize / linesizes[type] = foundInfo.linesize; info_p->cache_partitions[type] = foundInfo.partitions;aduäÇŸžcF/ýâË«”wda: þ Ä ™ ‚ b 1 û Ø ¯ y B  Õ Ð ” { q O -   é +Û +Ñ +Ç +¥ +— + +‚ +q +m +j +G + + +ô Æ • k ;  àܤ”nh2üÉ”_Y"ë´®w@ ɯ¨toB н¸‘jÿÞÀ¬”‘tX;úÔ¸›‰„a]* info_p->cache_sharing[type] = 1; info_p->cache_size[type] = foundInfo.totalsize; if (!foundDescriptor) continue; } } break; foundDescriptor = TRUE; type = foundInfo.type; foundInfo = nonDet_CacheInfo[j]; if (nonDet_CacheInfo[j].encoding == info_p->cache_info[i]) { for(j = 0; j < 43; j++) { foundDescriptor = FALSE; for (i = 0; i < 64; i++) { uint32_t cache_sets; uint32_t colors; cache_type_t type = Lnone; intel_nd_cache_info foundInfo; boolean_t foundDescriptor; /* mercurysquad: iterate over the predefined list of non-det cache info */ info_p->cpuid_cores_per_package = 1; if (!cpuid_deterministic_supported) { */ * something else * If deterministic cache parameters are not available, use /* } } vm_cache_geometry_colors = colors; if ( colors > vm_cache_geometry_colors ) colors = ( cache_linesize * cache_sets ) >> 12; */ * by the page offset. * The color is those bits in (set+offset) not covered * * +-----------------+-------+----------+ * VM: | don't care | color | pg offset| * +-----------------+-------+----------+ * * +-----------------+---------+--------+ * cache: | tag | set | offset | * +-----------------+---------+--------+ * * color, and other bits in the pageframe number: * To VM, it is composed of a page offset, a page * of a line offset, a set selector, and a tag. * physical address. To the cache, it is composed * To help visualize this, consider two views of a * * ( linesize * sets ) / page_size * which is: /* Compute the number of page colors for this cache, linesizes[type] = cache_linesize; info_p->cache_partitions[type] = cache_partitions; info_p->cache_sharing[type] = cache_sharing; info_p->cache_size[type] = cache_size; cache_associativity * cache_partitions; cache_size = cache_linesize * cache_sets * if (type != Lnone) { */ * ( linesize * sets * associativity * partitions ) /* The total size of a cache is: } type = Lnone; default: break; Lnone; type = cache_type == 3 ? L3U : case 3: break; Lnone; type = cache_type == 3 ? L2U : case 2: break; Lnone; cache_type == 2 ? L1I : type = cache_type == 1 ? L1D : case 1: switch (cache_level) { /* Map type/levels returned by CPUID into cache_type_t */ cache_sets = bitfield32(reg[ecx], 31, 0) + 1; cache_associativity = bitfield32(reg[ebx], 31, 22) + 1; cache_partitions = bitfield32(reg[ebx], 21, 12) + 1; cache_linesize = bitfield32(reg[ebx], 11, 0) + 1; = bitfield32(reg[eax], 31, 26) + 1; info_p->cpuid_cores_per_package cache_sharing = bitfield32(reg[eax], 25, 14) + 1; cache_level = bitfield32(reg[eax], 7, 5); break; /* no more caches */ if (cache_type == 0) cache_type = bitfield32(reg[eax], 4, 0);//kprintf("cpuid(4) index=%d eax=%p\n", index, reg[eax]); cpuid(reg); reg[ecx] = index; /* index starting at 0 */ reg[eax] = 4; /* cpuid request 4 */ uint32_t colors; uint32_t cache_partitions; uint32_t cache_size; uint32_t cache_associativity; uint32_t cache_sets; uint32_t cache_linesize; uint32_t cache_sharing; uint32_t cache_level; uint32_t cache_type; cache_type_t type = Lnone; for (index = 0; cpuid_deterministic_supported; index++) { cpuid_deterministic_supported = TRUE; if (cpuid_result[eax] >= 4) cpuid_fn(0, cpuid_result);ad«ÇþÇ“IGØÌ­lb`8 +Ö Í ² m = 3 *  ± € w U 8 6 5 ) ô ò Ñ © §  } 2 Ú +” + + +‰ +L +G +E + +÷ µ h _ @ ×nœ“Q?:#øÞ³ca;/þüáϾŒ{IH Û§¢†k.!çäŸpG= ÞТ‰†…:ôÉÄ */ * Loop over each cache on the processor. * Most processors Mac OS X supports implement this flavor of CPUID. * Get cache info using leaf 4, the "deterministic cache parameters." /* } } cpuid_result[j]; ((uint32_t *) info_p->cache_info)[4*i+j] = continue; if ((cpuid_result[j] >> 31) == 1) for (j = 0; j < 4; j++) { cpuid_fn(2, cpuid_result); break; if (i*16 > sizeof(info_p->cache_info)) for (i = 1; i < info_p->cache_info[0]; i++) { /* first byte gives number of cpuid calls to get all descriptors */ } ((uint32_t *) info_p->cache_info)[j] = cpuid_result[j]; continue; if ((cpuid_result[j] >> 31) == 1) /* bit31 is validity */ for (j = 0; j < 4; j++) { cpuid_fn(2, cpuid_result); */ * this internally, but must publish it for KEXTs. /* Get processor cache descriptor info using leaf 2. We don't use bzero( linesizes, sizeof(linesizes) ); boolean_t cpuid_deterministic_supported = FALSE; unsigned int j; unsigned int i; uint32_t linesizes[LCACHE_MAX]; uint32_t index; uint32_t reg[4]; uint32_t cpuid_result[4];{cpuid_set_cache_info( i386_cpu_info_t * info_p )static void/* this function is Intel-specific */} else panic("no linesize"); /* AMD machines should always report a cacheline */ info_p->cache_linesize = linesizes[L1D]; else if (linesizes[L1D]) info_p->cache_linesize = linesizes[L2U]; if ( linesizes[L2U] ) */ * else the L1D. * What linesize to publish? We use the L2 linesize if any, /* get_amd_cache_info(info_p, linesizes, L3U, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); get_amd_cache_info(info_p, linesizes, L2U, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); get_amd_cache_info(info_p, linesizes, L1D, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); get_amd_cache_info(info_p, linesizes, L1I, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); /* Get detailed cache info */ info_p->cpuid_logical_per_package = info_p->cpuid_cores_per_package; info_p->cpuid_cores_per_package = bitfield(reg[ecx], 7, 0) + 1; do_cpuid(0x80000008, reg); /* First get number of cores in the processor */ */ * Get deterministic cache info using AMD's cpuid registers /* */ * TODO: Implement AMD cache info to intel leaf-2 format conversion. * But apparently some kexts need this information (refer to the intel version below). /* AMD cpus don't support leaf-2 style cache info (it's annoying anyway). bzero( linesizes, sizeof(linesizes) ); uint32_t reg[4] = {0, 0, 0, 0}; uint32_t linesizes[LCACHE_MAX];{cpuid_set_amd_cache_info( i386_cpu_info_t * info_p )static void} *geometry_colors = colors; if ( colors > *geometry_colors ) colors = ( cache_linesize * cache_sets ) >> 12; cache_sets = cache_size / (cache_associativity * cache_linesize * cache_partitions); linesizes[type] = cache_linesize; } info_p->cpuid_cache_size = cache_size; info_p->cpuid_cache_L2_associativity = cache_associativity; if (type == L2U) { info_p->cache_partitions[type] = cache_partitions; info_p->cache_sharing[type] = cache_sharing; info_p->cache_size[type] = cache_size; } cache_associativity = 1ul << (cache_associativity / 2); if (cache_level > 1) { */ * The formula is 2 ^ (assoc / 2) /* For L2/L3 caches, AMD uses an encoding for associativity. cache_associativity = bitfield(reg[reg_to_use], 23, 16); cache_partition cache_linesize = bitfield3(reg[reg_to_use], 7, 0) cache_linesize = bitfield32(reg[reg_to_use], 7, 0); adž]Å yN'ذ‡^6æ ½ ” n =  ß ° ‡ ^ ;  ï É ¥ ] 9  î +È +£ +| +V +1 + +â » ” j B  îÆŸxQ,ã¾–nF"ñÀ`<ðʤ€\6ëÄvN'ÿسŽiDúÕ°Š`9ëÄž { 0xE2, CACHE, L3, 16, 2*M, 64 }, { 0xE0, CACHE, L3, 12, 18*M, 64 }, { 0xDF, CACHE, L3, 12, 12*M, 64 }, { 0xDE, CACHE, L3, 12, 6*M, 64 }, { 0xDD, CACHE, L3, 12, 3*M, 64 }, { 0xDC, CACHE, L3, 12, 1536*K, 64 }, { 0xDA, CACHE, L3, 8, 12*M, 64 }, { 0xD9, CACHE, L3, 8, 8*M, 64 }, { 0xD8, CACHE, L3, 8, 4*M, 64 }, { 0xD7, CACHE, L3, 8, 2*M, 64 }, { 0xD6, CACHE, L3, 8, 1*M, 64 }, { 0xD4, CACHE, L3, 4, 8*M, 64 }, { 0xD3, CACHE, L3, 4, 4*M, 64 }, { 0xD2, CACHE, L3, 4, 2*M, 64 }, { 0xD1, CACHE, L3, 4, 1*M, 64 }, { 0xD0, CACHE, L3, 4, 512*K, 64 }, { 0xCA, STLB, DATA1, 4, BOTH, 512 }, { 0xBA, TLB, DATA1, 4, BOTH, 64 }, { 0xB4, TLB, DATA1, 4, SMALL, 256 }, { 0xB3, TLB, DATA, 4, SMALL, 128 }, { 0xB2, TLB, INST, 4, SMALL, 64 }, { 0xB1, TLB, INST, 4, LARGE, 8 }, { 0xB0, TLB, INST, 4, SMALL, 128 }, { 0x87, CACHE, L2, 8, 1*M, 64 }, { 0x86, CACHE, L2, 4, 512*K, 64 }, { 0x85, CACHE, L2, 8, 2*M, 32 }, { 0x84, CACHE, L2, 8, 1*M, 32 }, { 0x83, CACHE, L2, 8, 512*K, 32 }, { 0x82, CACHE, L2, 8, 256*K, 32 }, { 0x80, CACHE, L2, 8, 512*K, 64 }, { 0x7F, CACHE, L2, 2, 512*K, 64 }, { 0x7D, CACHE, L2, 8, 2*M, 64 }, { 0x7C, CACHE, L2_2LINESECTOR, 8, 1*M, 64 }, { 0x7B, CACHE, L2_2LINESECTOR, 8, 512*K, 64 }, { 0x7A, CACHE, L2_2LINESECTOR, 8, 256*K, 64 }, { 0x79, CACHE, L2_2LINESECTOR, 8, 128*K, 64 }, { 0x78, CACHE, L2, 4, 1*M, 64 }, { 0x72, CACHE, TRACE, 8, 32*K, NA }, { 0x71, CACHE, TRACE, 8, 16*K, NA }, { 0x70, CACHE, TRACE, 8, 12*K, NA }, { 0x63, CACHE, L1, 4, 32*K, 64 }, { 0x62, CACHE, L1, 4, 16*K, 64 }, { 0x61, CACHE, L1, 4, 8*K, 64 }, { 0x60, CACHE, L1, 16*K, 8, 64 }, { 0x5D, TLB, DATA, NA, BOTH, 256 }, { 0x5C, TLB, DATA, NA, BOTH, 128 }, { 0x5B, TLB, DATA, NA, BOTH, 64 }, { 0x5A, TLB, DATA0, 4, LARGE, 32 }, { 0x59, TLB, DATA0, FULLY, SMALL, 16 }, { 0x57, TLB, DATA0, 4, SMALL, 16 }, { 0x56, TLB, DATA0, 4, LARGE, 16 }, { 0x55, TLB, INST, FULLY, BOTH, 7 }, { 0x52, TLB, INST, NA, BOTH, 256 }, { 0x51, TLB, INST, NA, BOTH, 128 }, { 0x50, TLB, INST, NA, BOTH, 64 }, { 0x4F, TLB, INST, NA, SMALL, 32 }, { 0x4E, CACHE, L2, 24, 6*M, 64 }, { 0x4D, CACHE, L3, 16, 16*M, 64 }, { 0x4C, CACHE, L3, 12, 12*M, 64 }, { 0x4B, CACHE, L3, 16, 8*M, 64 }, { 0x4A, CACHE, L3, 12, 6*M, 64 }, { 0x49, CACHE, L2, 16, 4*M, 64 }, { 0x48, CACHE, L2, 12, 3*M, 64 }, { 0x47, CACHE, L3, 8, 8*M, 64 }, { 0x46, CACHE, L3, 4, 4*M, 64 }, { 0x45, CACHE, L2, 4, 2*M, 32 }, { 0x44, CACHE, L2, 4, 1*M, 32 }, { 0x43, CACHE, L2, 4, 512*K, 32 }, { 0x42, CACHE, L2, 4, 256*K, 32 }, { 0x41, CACHE, L2, 4, 128*K, 32 }, { 0x40, CACHE, L2, NA, 0, NA }, { 0x30, CACHE, L1_INST, 8, 32*K, 64 }, { 0x2C, CACHE, L1_DATA, 8, 32*K, 64 }, { 0x29, CACHE, L3_2LINESECTOR, 8, 4*M, 64 }, { 0x25, CACHE, L3_2LINESECTOR, 8, 2*M, 64 }, { 0x23, CACHE, L3_2LINESECTOR, 8, 1*M, 64 }, { 0x22, CACHE, L3_2LINESECTOR, 4, 512*K, 64 }, { 0x21, CACHE, L2, 8, 256*K, 64 }, { 0x0E, CACHE, L1_DATA, 6, 24*K, 64 }, { 0x0D, CACHE, L1_DATA, 4, 16*K, 64 }, { 0x0C, CACHE, L1_DATA, 4, 16*K, 32 }, { 0x0B, TLB, INST, 4, LARGE, 4 }, { 0x0A, CACHE, L1_DATA, 2, 8*K, 32 }, { 0x09, CACHE, L1_INST, 4, 32*K, 64 }, { 0x08, CACHE, L1_INST, 4, 16*K, 32 }, { 0x06, CACHE, L1_INST, 4, 8*K, 32 }, { 0x05, TLB, DATA1, 4, LARGE, 32 }, { 0x04, TLB, DATA, 4, LARGE, 8 }, { 0x03, TLB, DATA, 4, SMALL, 64 }, { 0x02, TLB, INST, FULLY, LARGE, 2 }, { 0x01, TLB, INST, 4, SMALL, 32 }, { 0x00, _NULL_, NA, NA, NA, NA },// -------------------------------------------------------ad9õj¸|z5 ÿàŸ•“k= å   p f ] : ä ³ ª ˆ k i h \ ' %  Ü Ú ² ° e Ç + +À +¼ + +z +x +F +* +æ ™ q  Ÿ6ÍÄÀ‚pkT)ä”’l`/-ïν¬zyQP ØÓ·œ_RСxnQ5Ó¾º·¶²k%úõ */ * Loop over each cache on the processor. * Most processors Mac OS X supports implement this flavor of CPUID. * Get cache info using leaf 4, the "deterministic cache parameters." /* } } cpuid_result[j]; ((uint32_t *) info_p->cache_info)[4*i+j] = continue; if ((cpuid_result[j] >> 31) == 1) for (j = 0; j < 4; j++) { cpuid_fn(2, cpuid_result); break; if (i*16 > sizeof(info_p->cache_info)) for (i = 1; i < info_p->cache_info[0]; i++) { /* first byte gives number of cpuid calls to get all descriptors */ } ((uint32_t *) info_p->cache_info)[j] = cpuid_result[j]; continue; if ((cpuid_result[j] >> 31) == 1) /* bit31 is validity */ for (j = 0; j < 4; j++) { cpuid_fn(2, cpuid_result); */ * this internally, but must publish it for KEXTs. /* Get processor cache descriptor info using leaf 2. We don't use bzero( linesizes, sizeof(linesizes) ); boolean_t cpuid_deterministic_supported = FALSE; unsigned int j; unsigned int i; uint32_t linesizes[LCACHE_MAX]; uint32_t index; uint32_t reg[4]; uint32_t cpuid_result[4];{cpuid_set_cache_info( i386_cpu_info_t * info_p )static void/* this function is Intel-specific */} else panic("no linesize"); /* AMD machines should always report a cacheline */ info_p->cache_linesize = linesizes[L1D]; else if (linesizes[L1D]) info_p->cache_linesize = linesizes[L2U]; if ( linesizes[L2U] ) */ * else the L1D. * What linesize to publish? We use the L2 linesize if any, /* get_amd_cache_info(info_p, linesizes, L3U, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); get_amd_cache_info(info_p, linesizes, L2U, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); get_amd_cache_info(info_p, linesizes, L1D, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); get_amd_cache_info(info_p, linesizes, L1I, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); /* Get detailed cache info */ info_p->cpuid_logical_per_package = info_p->cpuid_cores_per_package; info_p->cpuid_cores_per_package = bitfield32(reg[ecx], 7, 0) + 1; do_cpuid(0x80000008, reg); /* First get number of cores in the processor */ */ * Get deterministic cache info using AMD's cpuid registers /* */ * TODO: Implement AMD cache info to intel leaf-2 format conversion. * But apparently some kexts need this information (refer to the intel version below). /* AMD cpus don't support leaf-2 style cache info (it's annoying anyway). bzero( linesizes, sizeof(linesizes) ); uint32_t reg[4] = {0, 0, 0, 0}; uint32_t linesizes[LCACHE_MAX];{cpuid_set_amd_cache_info( i386_cpu_info_t * info_p )static void} *geometry_colors = colors; if ( colors > *geometry_colors ) colors = ( cache_linesize * cache_sets ) >> 12; cache_sets = cache_size / (cache_associativity * cache_linesize * cache_partitions); linesizes[type] = cache_linesize; } info_p->cpuid_cache_size = cache_size; info_p->cpuid_cache_L2_associativity = cache_associativity; if (type == L2U) { info_p->cache_partitions[type] = cache_partitions; info_p->cache_sharing[type] = cache_sharing; info_p->cache_size[type] = cache_size; } cache_associativity = 1ul << (cache_associativity / 2); if (cache_level > 1) { */ * The formula is 2 ^ (assoc / 2) /* For L2/L3 caches, AMD uses an encoding for associativity. cache_associativity = bitfield32(reg[reg_to_use], 23, 16); cache_partitions = bitfield32(reg[reg_to_use], 15, 8); // Needs review \ No newline at end of file diff -Naur xnu-1486.2.11.orig/osfmk/i386/AT386/model_dep.c xnu-1486.2.11/osfmk/i386/AT386/model_dep.c --- xnu-1486.2.11.orig/osfmk/i386/AT386/model_dep.c 2009-11-12 12:59:55.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/AT386/model_dep.c 2009-11-16 22:57:59.000000000 -0500 @@ -114,6 +114,7 @@ #include #include #include +#include // qoopz: chud kexts have cpuids, must remove them for AMD #include @@ -684,17 +685,28 @@ int reset_mem_on_reboot = 1; +#define MACH_REBOOT_MSG "MACH Reboot: You can reset your computer now\n" +#define MACH_HALT_MSG "CPU halted: It's now safe to turn off your computer\n" + /* * Halt the system or reboot. */ void halt_all_cpus(boolean_t reboot) { + /* ovof / paulicat: Disable all cores on shutdown to prevent the system hanging */ + uint32_t ncpus, i; + ncpus = chudxnu_logical_cpu_count(); + for (i = 0; i < ncpus; i++) + chudxnu_enable_cpu(i, FALSE); if (reboot) { - printf("MACH Reboot\n"); + printf(MACH_REBOOT_MSG); PEHaltRestart( kPERestartCPU ); + asm volatile ("movb $0xfe, %al\n" + "outb %al, $0x64\n" + "hlt\n"); } else { - printf("CPU halted\n"); + printf(MACH_HALT_MSG); PEHaltRestart( kPEHaltCPU ); } while(1); @@ -992,7 +1004,7 @@ int frame_index; volatile uint32_t *ppbtcnt = &pbtcnt; uint64_t bt_tsc_timeout; - boolean_t keepsyms = FALSE; + boolean_t keepsyms = TRUE; /* mercurysquad: changed default to TRUE */ if(pbtcpu != cpu_number()) { hw_atomic_add(&pbtcnt, 1); diff -Naur xnu-1486.2.11.orig/osfmk/i386/acpi_wakeup.s xnu-1486.2.11/osfmk/i386/acpi_wakeup.s --- xnu-1486.2.11.orig/osfmk/i386/acpi_wakeup.s 2009-11-12 13:00:01.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/acpi_wakeup.s 2009-11-16 22:57:59.000000000 -0500 @@ -31,6 +31,7 @@ #include #include #include +#include // mercurysquad: needed for CPUID_EXTFEATURE_XD .file "acpi_wakeup.s" @@ -130,10 +131,18 @@ movl %ebx, %cr3 movl %ecx, %cr4 + /* mercurysquad: check for NXE support and skip setting NXE if not supported */ + movl $0x80000001, %eax /* set eax to get feature bits */ + cpuid /* Get cpuid */ + test $(CPUID_EXTFEATURE_XD), %edx /* Test for NXE support */ + jz 1f /* Not supported, skip NXE */ + + /* Otherwise set NXE bit */ movl $(MSR_IA32_EFER), %ecx /* MSR number in ecx */ rdmsr /* MSR value return in edx: eax */ orl $(MSR_IA32_EFER_NXE), %eax /* Set NXE bit in low 32-bits */ wrmsr /* Update Extended Feature Enable reg */ +1: /* restore kernel GDT */ lgdt PA(saved_gdt) diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/bcopy_sse3_64.s xnu-1486.2.11/osfmk/i386/commpage/bcopy_sse3_64.s --- xnu-1486.2.11.orig/osfmk/i386/commpage/bcopy_sse3_64.s 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/bcopy_sse3_64.s 2009-11-16 22:57:59.000000000 -0500 @@ -0,0 +1,372 @@ +#include +#include +//qoopz: see bcopy_sse3x_64.s +/* + * The bcopy/memcpy loops, tuned for 64-bit Pentium-M class processors with + * Supplemental SSE3 and 64-byte cache lines. This is the 64-bit version. + * + * The following #defines are tightly coupled to the u-architecture: + */ + +#define kShort 80 // too short to bother with SSE (must be >=80) +#define kVeryLong (500*1024) // large enough for non-temporal stores (>=8192 and <2GB) +#define kFastUCode ((16*1024)-15) // cutoff for microcode fastpath for "rep/movsl" + + +// void bcopy(const void *src, void *dst, size_t len); +COMMPAGE_FUNCTION_START(bcopy_sse3_64, 64, 5) +LZero: + pushq %rbp // set up a frame for backtraces + movq %rsp,%rbp + movq %rsi,%rax // copy dest ptr + movq %rdi,%rsi // xchange source and dest ptrs + movq %rax,%rdi + subq %rsi,%rax // (dest - source) + cmpq %rdx,%rax // must move in reverse if (dest - source) < length + jb LReverseIsland + cmpq $(kShort),%rdx // long enough to bother with SSE? + jbe LShort // no + jmp LNotShort + +// +// void *memcpy(void *dst, const void *src, size_t len); +// void *memmove(void *dst, const void *src, size_t len); +// +// NB: These need to be 32 bytes from bcopy(): +// + + .align 5, 0x90 +Lmemcpy: // void *memcpy(void *dst, const void *src, size_t len) +Lmemmove: // void *memmove(void *dst, const void *src, size_t len) + pushq %rbp // set up a frame for backtraces + movq %rsp,%rbp + movq %rdi,%r11 // save return value here + movq %rdi,%rax + subq %rsi,%rax // (dest - source) + cmpq %rdx,%rax // must move in reverse if (dest - source) < length + jb LReverseIsland + cmpq $(kShort),%rdx // long enough to bother with SSE? + ja LNotShort // yes + +// Handle short forward copies. As the most common case, this is the fall-through path. +// rdx = length (<= kShort) +// rsi = source ptr +// rdi = dest ptr + +LShort: + movl %edx,%ecx // copy length using 32-bit operation + shrl $2,%ecx // get #doublewords + jz LLeftovers +2: // loop copying doublewords + movl (%rsi),%eax + addq $4,%rsi + movl %eax,(%rdi) + addq $4,%rdi + decl %ecx + jnz 2b +LLeftovers: // handle leftover bytes (0..3) in last word + andl $3,%edx // any leftover bytes? + jz 5f +4: // loop copying bytes + movb (%rsi),%al + incq %rsi + movb %al,(%rdi) + incq %rdi + decl %edx + jnz 4b +5: + movq %r11,%rax // get return value (dst ptr) for memcpy/memmove + popq %rbp + ret + + +LReverseIsland: // keep the "jb" above a short branch... + jmp LReverse // ...because reverse moves are uncommon + + +// Handle forward moves that are long enough to justify use of SSE. +// First, 16-byte align the destination. +// rdx = length (> kShort) +// rsi = source ptr +// rdi = dest ptr + +LNotShort: + cmpq $(kVeryLong),%rdx // long enough to justify heavyweight loops? + jae LVeryLong // use very-long-operand path + movl %edi,%ecx // copy low half of destination ptr + negl %ecx + andl $15,%ecx // get #bytes to align destination + jz LDestAligned // already aligned + subl %ecx,%edx // decrement length + rep // align destination + movsb + + +// Destination is now aligned. Dispatch to one of sixteen loops over 64-byte chunks, +// based on the alignment of the source. All vector loads and stores are aligned. +// Even though this means we have to shift and repack vectors, doing so is much faster +// than unaligned loads. Since kShort>=80 and we've moved at most 15 bytes already, +// there is at least one chunk. When we enter the copy loops, the following registers +// are set up: +// rdx = residual length (0..63) +// rcx = -(length to move), a multiple of 64 less than 2GB +// rsi = ptr to 1st source byte not to move (unaligned) +// rdi = ptr to 1st dest byte not to move (aligned) + +LDestAligned: + movl %edx,%ecx // copy length + movl %esi,%eax // copy low half of source address + andl $63,%edx // get remaining bytes for LShort + andl $15,%eax // mask to low 4 bits of source address + andl $-64,%ecx // get number of bytes we will copy in inner loop + + +// We'd like to use lea with rip-relative addressing, but cannot in a .code64 block in +// a 32-bit object file (4586528). Generate the leaq opcode manually. +#if defined(__i386__) + .byte 0x4c + .byte 0x8d + .byte 0x05 + .long LTable-LRIP +LRIP: +#elif defined(__x86_64__) + leaq LTable(%rip), %r8 +#else +#error Unsupported architecture +#endif + addq %rcx,%rsi // point to 1st byte not copied + addq %rcx,%rdi + movl (%r8,%rax,4),%eax // get offset of routine + negq %rcx // now generate offset to 1st byte to be copied + addq %r8,%rax // generate address of copy loop + jmp *%rax // enter copy loop, selected by source alignment + + .align 2 +LTable: // table of copy loop addresses +// force generation of assembly-time constants. Otherwise assembler +// creates subtractor relocations relative to first external symbol, +// and this file has none +.set LMod0Offset, LMod0 - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable +.set LMod0uOffset, LMod0u - LTable + .long LMod0Offset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + .long LMod0uOffset + +// Very long forward moves. These are at least several pages. They are special cased +// and aggressively optimized, not so much because they are common or useful, but +// because they are subject to benchmark. There isn't enough room for them in the +// area reserved on the commpage for bcopy, so we put them elsewhere. We call +// the longcopy routine using the normal ABI: +// rdi = dest +// rsi = source +// rdx = length (>= kVeryLong bytes) + +LVeryLong: + pushq %r11 // save return value + movq $_COMM_PAGE_32_TO_64(_COMM_PAGE_LONGCOPY),%rax + call *%rax // call very long operand routine + popq %rax // pop return value + popq %rbp + ret + + +// On Pentium-M, the microcode for "rep/movsl" is faster than SSE for 16-byte +// aligned operands from about 32KB up to kVeryLong for the hot cache case, and from +// about 256 bytes up to kVeryLong for cold caches. This is because the microcode +// avoids having to read destination cache lines that will be completely overwritten. +// The cutoff we use (ie, kFastUCode) must somehow balance the two cases, since +// we do not know if the destination is in cache or not. + +Lfastpath: + addq %rcx,%rsi // restore ptrs to 1st byte of source and dest + addq %rcx,%rdi + negl %ecx // make length positive (known to be < 2GB) + orl %edx,%ecx // restore total #bytes remaining to move + cld // we'll move forward + shrl $2,%ecx // compute #words to move + rep // the u-code will optimize this + movsl + jmp LLeftovers // handle 0..3 leftover bytes + + +// Forward loop for medium length operands in which low four bits of %rsi == 0000 + +LMod0: + cmpl $(-kFastUCode),%ecx // %rcx == -length, where (length < kVeryLong) + jle Lfastpath // long enough for fastpath in microcode + jmp 1f + .align 4,0x90 // 16-byte align inner loops +1: // loop over 64-byte chunks + movdqa (%rsi,%rcx),%xmm0 + movdqa 16(%rsi,%rcx),%xmm1 + movdqa 32(%rsi,%rcx),%xmm2 + movdqa 48(%rsi,%rcx),%xmm3 + + movdqa %xmm0,(%rdi,%rcx) + movdqa %xmm1,16(%rdi,%rcx) + movdqa %xmm2,32(%rdi,%rcx) + movdqa %xmm3,48(%rdi,%rcx) + + addq $64,%rcx + jnz 1b + + jmp LShort // copy remaining 0..63 bytes and done + +// mifki / netkas: based on LMod0 +// Forward loop for medium length operands in which low four bits of %rsi == 0000 + + .align 4,0x90 // 16-byte align inner loops +LMod0u: + movdqu (%rsi,%rcx),%xmm0 + movdqu 16(%rsi,%rcx),%xmm1 + movdqu 32(%rsi,%rcx),%xmm2 + movdqu 48(%rsi,%rcx),%xmm3 + + movdqa %xmm0,(%rdi,%rcx) + movdqa %xmm1,16(%rdi,%rcx) + movdqa %xmm2,32(%rdi,%rcx) + movdqa %xmm3,48(%rdi,%rcx) + + addq $64,%rcx + jnz LMod0u // loop over 64-byte chunks + + jmp LShort // copy remaining 0..63 bytes and done + +// Reverse moves. These are not optimized as aggressively as their forward +// counterparts, as they are only used with destructive overlap. +// rdx = length +// rsi = source ptr +// rdi = dest ptr + +LReverse: + addq %rdx,%rsi // point to end of strings + addq %rdx,%rdi + cmpq $(kShort),%rdx // long enough to bother with SSE? + ja LReverseNotShort // yes + +// Handle reverse short copies. +// edx = length (<= kShort) +// rsi = one byte past end of source +// rdi = one byte past end of dest + +LReverseShort: + movl %edx,%ecx // copy length + shrl $3,%ecx // #quadwords + jz 3f +1: + subq $8,%rsi + movq (%rsi),%rax + subq $8,%rdi + movq %rax,(%rdi) + decl %ecx + jnz 1b +3: + andl $7,%edx // bytes? + jz 5f +4: + decq %rsi + movb (%rsi),%al + decq %rdi + movb %al,(%rdi) + decl %edx + jnz 4b +5: + movq %r11,%rax // get return value (dst ptr) for memcpy/memmove + popq %rbp + ret + +// Handle a reverse move long enough to justify using SSE. +// rdx = length (> kShort) +// rsi = one byte past end of source +// rdi = one byte past end of dest + +LReverseNotShort: + movl %edi,%ecx // copy destination + andl $15,%ecx // get #bytes to align destination + je LReverseDestAligned // already aligned + subq %rcx,%rdx // adjust length +1: // loop copying 1..15 bytes + decq %rsi + movb (%rsi),%al + decq %rdi + movb %al,(%rdi) + decl %ecx + jnz 1b + +// Destination is now aligned. Prepare for reverse loops. + +LReverseDestAligned: + movq %rdx,%rcx // copy length + andl $63,%edx // get remaining bytes for LReverseShort + andq $-64,%rcx // get number of bytes we will copy in inner loop + subq %rcx,%rsi // point to endpoint of copy + subq %rcx,%rdi + testl $15,%esi // is source aligned too? + jnz LReverseUnalignedLoop // no + +LReverseAlignedLoop: // loop over 64-byte chunks + movdqa -16(%rsi,%rcx),%xmm0 + movdqa -32(%rsi,%rcx),%xmm1 + movdqa -48(%rsi,%rcx),%xmm2 + movdqa -64(%rsi,%rcx),%xmm3 + + movdqa %xmm0,-16(%rdi,%rcx) + movdqa %xmm1,-32(%rdi,%rcx) + movdqa %xmm2,-48(%rdi,%rcx) + movdqa %xmm3,-64(%rdi,%rcx) + + subq $64,%rcx + jne LReverseAlignedLoop + + jmp LReverseShort // copy remaining 0..63 bytes and done + + +// Reverse, unaligned loop. LDDQU==MOVDQU on these machines. + +LReverseUnalignedLoop: // loop over 64-byte chunks + movdqu -16(%rsi,%rcx),%xmm0 + movdqu -32(%rsi,%rcx),%xmm1 + movdqu -48(%rsi,%rcx),%xmm2 + movdqu -64(%rsi,%rcx),%xmm3 + + movdqa %xmm0,-16(%rdi,%rcx) + movdqa %xmm1,-32(%rdi,%rcx) + movdqa %xmm2,-48(%rdi,%rcx) + movdqa %xmm3,-64(%rdi,%rcx) + + subq $64,%rcx + jne LReverseUnalignedLoop + + jmp LReverseShort // copy remaining 0..63 bytes and done + + + /* turbo: kCache64 flag shouldn't be specified to avoid issues on Pentium D */ + COMMPAGE_DESCRIPTOR(bcopy_sse3_64,_COMM_PAGE_BCOPY,kHasSSE3,kHasSupplementalSSE3) + diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/commpage.c xnu-1486.2.11/osfmk/i386/commpage/commpage.c --- xnu-1486.2.11.orig/osfmk/i386/commpage/commpage.c 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/commpage.c 2009-11-16 22:57:59.000000000 -0500 @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,9 @@ #include +#include +#include + /* the lists of commpage routines are in commpage_asm.s */ extern commpage_descriptor* commpage_32_routines[]; extern commpage_descriptor* commpage_64_routines[]; @@ -324,7 +328,7 @@ if (rd->commpage_address != cur_routine) { if ((cur_routine!=0) && (matched==0)) - panic("commpage no match for last, next address %08x", rd->commpage_address); +// panic("commpage no match for last, next address %08x", rd->commpage_address); // qoopz: panics on some pentium d cur_routine = rd->commpage_address; matched = 0; } @@ -426,6 +430,12 @@ next = 0; commpage_stuff_routine(&sigdata_descriptor); } + + /* mercurysquad: check that the RTC granularity was properly initialized, halt if not. + * This is a bizarrely random place to put this, but I need to figure out how to print a + * message very early on in the boot process.*/ + if (kTscPanicOn) + panic("rtclock_init panic"); } @@ -452,6 +462,16 @@ #ifndef __LP64__ pmap_commpage32_init((vm_offset_t) commPagePtr32, _COMM_PAGE32_BASE_ADDRESS, _COMM_PAGE32_AREA_USED/INTEL_PGBYTES); + /* mercurysquad: enable emulator in 32bit if CPU doesn't support SSE3 */ + if (!sse3emu_size) + printf("warning: kernel not built with SSE3 emulator, won't attempt detection\n"); + else if (!(cpuid_info()->cpuid_features & CPUID_FEATURE_SSE3)) { + printf("Enabling SSE3 emulator..."); + /* Install into commpage. Actual patching of master_idt happens in start.s */ + ASSERT(sse3emu_size == PAGE_SIZE); + commpage_stuff2(_COMM_PAGE_SSE3EMU, &sse3emu_data, PAGE_SIZE, TRUE); + printf("done.\n"); + } #endif time_data64 = time_data32; /* if no 64-bit commpage, point to 32-bit */ diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/commpage.c.orig xnu-1486.2.11/osfmk/i386/commpage/commpage.c.orig --- xnu-1486.2.11.orig/osfmk/i386/commpage/commpage.c.orig 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/commpage.c.orig 2009-11-16 22:29:44.000000000 -0500 @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2003-2008 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@ + */ + +/* + * Here's what to do if you want to add a new routine to the comm page: + * + * 1. Add a definition for it's address in osfmk/i386/cpu_capabilities.h, + * being careful to reserve room for future expansion. + * + * 2. Write one or more versions of the routine, each with it's own + * commpage_descriptor. The tricky part is getting the "special", + * "musthave", and "canthave" fields right, so that exactly one + * version of the routine is selected for every machine. + * The source files should be in osfmk/i386/commpage/. + * + * 3. Add a ptr to your new commpage_descriptor(s) in the "routines" + * array in osfmk/i386/commpage/commpage_asm.s. There are two + * arrays, one for the 32-bit and one for the 64-bit commpage. + * + * 4. Write the code in Libc to use the new routine. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* the lists of commpage routines are in commpage_asm.s */ +extern commpage_descriptor* commpage_32_routines[]; +extern commpage_descriptor* commpage_64_routines[]; + +/* translated commpage descriptors from commpage_sigs.c */ +extern commpage_descriptor sigdata_descriptor; +extern commpage_descriptor *ba_descriptors[]; + +extern vm_map_t commpage32_map; // the shared submap, set up in vm init +extern vm_map_t commpage64_map; // the shared submap, set up in vm init + +char *commPagePtr32 = NULL; // virtual addr in kernel map of 32-bit commpage +char *commPagePtr64 = NULL; // ...and of 64-bit commpage +int _cpu_capabilities = 0; // define the capability vector + +int noVMX = 0; /* if true, do not set kHasAltivec in ppc _cpu_capabilities */ + +typedef uint32_t commpage_address_t; + +static commpage_address_t next; // next available address in comm page +static commpage_address_t cur_routine; // comm page address of "current" routine +static boolean_t matched; // true if we've found a match for "current" routine + +static char *commPagePtr; // virtual addr in kernel map of commpage we are working on +static commpage_address_t commPageBaseOffset; // subtract from 32-bit runtime address to get offset in virtual commpage in kernel map + +static commpage_time_data *time_data32 = NULL; +static commpage_time_data *time_data64 = NULL; + +/* Allocate the commpage and add to the shared submap created by vm: + * 1. allocate a page in the kernel map (RW) + * 2. wire it down + * 3. make a memory entry out of it + * 4. map that entry into the shared comm region map (R-only) + */ + +static void* +commpage_allocate( + vm_map_t submap, // commpage32_map or commpage_map64 + size_t area_used ) // _COMM_PAGE32_AREA_USED or _COMM_PAGE64_AREA_USED +{ + vm_offset_t kernel_addr = 0; // address of commpage in kernel map + vm_offset_t zero = 0; + vm_size_t size = area_used; // size actually populated + vm_map_entry_t entry; + ipc_port_t handle; + + if (submap == NULL) + panic("commpage submap is null"); + + if (vm_map(kernel_map,&kernel_addr,area_used,0,VM_FLAGS_ANYWHERE,NULL,0,FALSE,VM_PROT_ALL,VM_PROT_ALL,VM_INHERIT_NONE)) + panic("cannot allocate commpage"); + + if (vm_map_wire(kernel_map,kernel_addr,kernel_addr+area_used,VM_PROT_DEFAULT,FALSE)) + panic("cannot wire commpage"); + + /* + * Now that the object is created and wired into the kernel map, mark it so that no delay + * copy-on-write will ever be performed on it as a result of mapping it into user-space. + * If such a delayed copy ever occurred, we could remove the kernel's wired mapping - and + * that would be a real disaster. + * + * JMM - What we really need is a way to create it like this in the first place. + */ + if (!vm_map_lookup_entry( kernel_map, vm_map_trunc_page(kernel_addr), &entry) || entry->is_sub_map) + panic("cannot find commpage entry"); + entry->object.vm_object->copy_strategy = MEMORY_OBJECT_COPY_NONE; + + if (mach_make_memory_entry( kernel_map, // target map + &size, // size + kernel_addr, // offset (address in kernel map) + VM_PROT_ALL, // map it RWX + &handle, // this is the object handle we get + NULL )) // parent_entry (what is this?) + panic("cannot make entry for commpage"); + + if (vm_map_64( submap, // target map (shared submap) + &zero, // address (map into 1st page in submap) + area_used, // size + 0, // mask + VM_FLAGS_FIXED, // flags (it must be 1st page in submap) + handle, // port is the memory entry we just made + 0, // offset (map 1st page in memory entry) + FALSE, // copy + VM_PROT_READ|VM_PROT_EXECUTE, // cur_protection (R-only in user map) + VM_PROT_READ|VM_PROT_EXECUTE, // max_protection + VM_INHERIT_SHARE )) // inheritance + panic("cannot map commpage"); + + ipc_port_release(handle); + + return (void*)(intptr_t)kernel_addr; // return address in kernel map +} + +/* Get address (in kernel map) of a commpage field. */ + +static void* +commpage_addr_of( + commpage_address_t addr_at_runtime ) +{ + return (void*) ((uintptr_t)commPagePtr + (addr_at_runtime - commPageBaseOffset)); +} + +/* Determine number of CPUs on this system. We cannot rely on + * machine_info.max_cpus this early in the boot. + */ +static int +commpage_cpus( void ) +{ + int cpus; + + cpus = ml_get_max_cpus(); // NB: this call can block + + if (cpus == 0) + panic("commpage cpus==0"); + if (cpus > 0xFF) + cpus = 0xFF; + + return cpus; +} + +/* Initialize kernel version of _cpu_capabilities vector (used by KEXTs.) */ + +static void +commpage_init_cpu_capabilities( void ) +{ + int bits; + int cpus; + ml_cpu_info_t cpu_info; + + bits = 0; + ml_cpu_get_info(&cpu_info); + + switch (cpu_info.vector_unit) { + case 8: + bits |= kHasSSE4_2; + /* fall thru */ + case 7: + bits |= kHasSSE4_1; + /* fall thru */ + case 6: + bits |= kHasSupplementalSSE3; + /* fall thru */ + case 5: + bits |= kHasSSE3; + /* fall thru */ + case 4: + bits |= kHasSSE2; + /* fall thru */ + case 3: + bits |= kHasSSE; + /* fall thru */ + case 2: + bits |= kHasMMX; + default: + break; + } + switch (cpu_info.cache_line_size) { + case 128: + bits |= kCache128; + break; + case 64: + bits |= kCache64; + break; + case 32: + bits |= kCache32; + break; + default: + break; + } + cpus = commpage_cpus(); // how many CPUs do we have + + if (cpus == 1) + bits |= kUP; + + bits |= (cpus << kNumCPUsShift); + + bits |= kFastThreadLocalStorage; // we use %gs for TLS + + if (cpu_mode_is64bit()) // k64Bit means processor is 64-bit capable + bits |= k64Bit; + + if (tscFreq <= SLOW_TSC_THRESHOLD) /* is TSC too slow for _commpage_nanotime? */ + bits |= kSlow; + + _cpu_capabilities = bits; // set kernel version for use by drivers etc +} + +int +_get_cpu_capabilities(void) +{ + return _cpu_capabilities; +} + +/* Copy data into commpage. */ + +static void +commpage_stuff( + commpage_address_t address, + const void *source, + int length ) +{ + void *dest = commpage_addr_of(address); + + if (address < next) + panic("commpage overlap at address 0x%p, 0x%x < 0x%x", dest, address, next); + + bcopy(source,dest,length); + + next = address + length; +} + +static void +commpage_stuff_swap( + commpage_address_t address, + void *source, + int length, + int legacy ) +{ + if ( legacy ) { + void *dest = commpage_addr_of(address); + dest = (void *)((uintptr_t) dest + _COMM_PAGE_SIGS_OFFSET); + switch (length) { + case 2: + OSWriteSwapInt16(dest, 0, *(uint16_t *)source); + break; + case 4: + OSWriteSwapInt32(dest, 0, *(uint32_t *)source); + break; + case 8: + OSWriteSwapInt64(dest, 0, *(uint64_t *)source); + break; + } + } +} + +static void +commpage_stuff2( + commpage_address_t address, + void *source, + int length, + int legacy ) +{ + commpage_stuff_swap(address, source, length, legacy); + commpage_stuff(address, source, length); +} + +/* Copy a routine into comm page if it matches running machine. + */ +static void +commpage_stuff_routine( + commpage_descriptor *rd ) +{ + uint32_t must,cant; + + if (rd->commpage_address != cur_routine) { + if ((cur_routine!=0) && (matched==0)) + panic("commpage no match for last, next address %08x", rd->commpage_address); + cur_routine = rd->commpage_address; + matched = 0; + } + + must = _cpu_capabilities & rd->musthave; + cant = _cpu_capabilities & rd->canthave; + + if ((must == rd->musthave) && (cant == 0)) { + if (matched) + panic("commpage multiple matches for address %08x", rd->commpage_address); + matched = 1; + + commpage_stuff(rd->commpage_address,rd->code_address,rd->code_length); + } +} + +/* Fill in the 32- or 64-bit commpage. Called once for each. + * The 32-bit ("legacy") commpage has a bunch of stuff added to it + * for translated processes, some of which is byte-swapped. + */ + +static void +commpage_populate_one( + vm_map_t submap, // commpage32_map or compage64_map + char ** kernAddressPtr, // &commPagePtr32 or &commPagePtr64 + size_t area_used, // _COMM_PAGE32_AREA_USED or _COMM_PAGE64_AREA_USED + commpage_address_t base_offset, // will become commPageBaseOffset + commpage_descriptor** commpage_routines, // list of routine ptrs for this commpage + boolean_t legacy, // true if 32-bit commpage + commpage_time_data** time_data, // &time_data32 or &time_data64 + const char* signature ) // "commpage 32-bit" or "commpage 64-bit" +{ + short c2; + int c4; + static double two52 = 1048576.0 * 1048576.0 * 4096.0; // 2**52 + static double ten6 = 1000000.0; // 10**6 + commpage_descriptor **rd; + short version = _COMM_PAGE_THIS_VERSION; + int swapcaps; + + next = 0; + cur_routine = 0; + commPagePtr = (char *)commpage_allocate( submap, (vm_size_t) area_used ); + *kernAddressPtr = commPagePtr; // save address either in commPagePtr32 or 64 + commPageBaseOffset = base_offset; + + *time_data = commpage_addr_of( _COMM_PAGE_TIME_DATA_START ); + + /* Stuff in the constants. We move things into the comm page in strictly + * ascending order, so we can check for overlap and panic if so. + */ + commpage_stuff(_COMM_PAGE_SIGNATURE,signature,(int)strlen(signature)); + commpage_stuff2(_COMM_PAGE_VERSION,&version,sizeof(short),legacy); + commpage_stuff(_COMM_PAGE_CPU_CAPABILITIES,&_cpu_capabilities,sizeof(int)); + + /* excuse our magic constants, we cannot include ppc/cpu_capabilities.h */ + /* always set kCache32 and kDcbaAvailable */ + swapcaps = 0x44; + if ( _cpu_capabilities & kUP ) + swapcaps |= (kUP + (1 << kNumCPUsShift)); + else + swapcaps |= 2 << kNumCPUsShift; /* limit #cpus to 2 */ + if ( ! noVMX ) /* if rosetta will be emulating altivec... */ + swapcaps |= 0x101; /* ...then set kHasAltivec and kDataStreamsAvailable too */ + commpage_stuff_swap(_COMM_PAGE_CPU_CAPABILITIES, &swapcaps, sizeof(int), legacy); + c2 = 32; + commpage_stuff_swap(_COMM_PAGE_CACHE_LINESIZE,&c2,2,legacy); + + if (_cpu_capabilities & kCache32) + c2 = 32; + else if (_cpu_capabilities & kCache64) + c2 = 64; + else if (_cpu_capabilities & kCache128) + c2 = 128; + commpage_stuff(_COMM_PAGE_CACHE_LINESIZE,&c2,2); + + c4 = MP_SPIN_TRIES; + commpage_stuff(_COMM_PAGE_SPIN_COUNT,&c4,4); + + if ( legacy ) { + commpage_stuff2(_COMM_PAGE_2_TO_52,&two52,8,legacy); + commpage_stuff2(_COMM_PAGE_10_TO_6,&ten6,8,legacy); + } + + for( rd = commpage_routines; *rd != NULL ; rd++ ) + commpage_stuff_routine(*rd); + + if (!matched) + panic("commpage no match on last routine"); + + if (next > _COMM_PAGE_END) + panic("commpage overflow: next = 0x%08x, commPagePtr = 0x%p", next, commPagePtr); + + if ( legacy ) { + next = 0; + for( rd = ba_descriptors; *rd != NULL ; rd++ ) + commpage_stuff_routine(*rd); + + next = 0; + commpage_stuff_routine(&sigdata_descriptor); + } +} + + +/* Fill in commpages: called once, during kernel initialization, from the + * startup thread before user-mode code is running. + * + * See the top of this file for a list of what you have to do to add + * a new routine to the commpage. + */ + +void +commpage_populate( void ) +{ + commpage_init_cpu_capabilities(); + + commpage_populate_one( commpage32_map, + &commPagePtr32, + _COMM_PAGE32_AREA_USED, + _COMM_PAGE32_BASE_ADDRESS, + commpage_32_routines, + TRUE, /* legacy (32-bit) commpage */ + &time_data32, + "commpage 32-bit"); +#ifndef __LP64__ + pmap_commpage32_init((vm_offset_t) commPagePtr32, _COMM_PAGE32_BASE_ADDRESS, + _COMM_PAGE32_AREA_USED/INTEL_PGBYTES); +#endif + time_data64 = time_data32; /* if no 64-bit commpage, point to 32-bit */ + + if (_cpu_capabilities & k64Bit) { + commpage_populate_one( commpage64_map, + &commPagePtr64, + _COMM_PAGE64_AREA_USED, + _COMM_PAGE32_START_ADDRESS, /* commpage address are relative to 32-bit commpage placement */ + commpage_64_routines, + FALSE, /* not a legacy commpage */ + &time_data64, + "commpage 64-bit"); +#ifndef __LP64__ + pmap_commpage64_init((vm_offset_t) commPagePtr64, _COMM_PAGE64_BASE_ADDRESS, + _COMM_PAGE64_AREA_USED/INTEL_PGBYTES); +#endif + } + + rtc_nanotime_init_commpage(); +} + + +/* Update commpage nanotime information. Note that we interleave + * setting the 32- and 64-bit commpages, in order to keep nanotime more + * nearly in sync between the two environments. + * + * This routine must be serialized by some external means, ie a lock. + */ + +void +commpage_set_nanotime( + uint64_t tsc_base, + uint64_t ns_base, + uint32_t scale, + uint32_t shift ) +{ + commpage_time_data *p32 = time_data32; + commpage_time_data *p64 = time_data64; + static uint32_t generation = 0; + uint32_t next_gen; + + if (p32 == NULL) /* have commpages been allocated yet? */ + return; + + if ( generation != p32->nt_generation ) + panic("nanotime trouble 1"); /* possibly not serialized */ + if ( ns_base < p32->nt_ns_base ) + panic("nanotime trouble 2"); + if ((shift != 32) && ((_cpu_capabilities & kSlow)==0) ) + panic("nanotime trouble 3"); + + next_gen = ++generation; + if (next_gen == 0) + next_gen = ++generation; + + p32->nt_generation = 0; /* mark invalid, so commpage won't try to use it */ + p64->nt_generation = 0; + + p32->nt_tsc_base = tsc_base; + p64->nt_tsc_base = tsc_base; + + p32->nt_ns_base = ns_base; + p64->nt_ns_base = ns_base; + + p32->nt_scale = scale; + p64->nt_scale = scale; + + p32->nt_shift = shift; + p64->nt_shift = shift; + + p32->nt_generation = next_gen; /* mark data as valid */ + p64->nt_generation = next_gen; +} + + +/* Disable commpage gettimeofday(), forcing commpage to call through to the kernel. */ + +void +commpage_disable_timestamp( void ) +{ + time_data32->gtod_generation = 0; + time_data64->gtod_generation = 0; +} + + +/* Update commpage gettimeofday() information. As with nanotime(), we interleave + * updates to the 32- and 64-bit commpage, in order to keep time more nearly in sync + * between the two environments. + * + * This routine must be serializeed by some external means, ie a lock. + */ + + void + commpage_set_timestamp( + uint64_t abstime, + uint64_t secs ) +{ + commpage_time_data *p32 = time_data32; + commpage_time_data *p64 = time_data64; + static uint32_t generation = 0; + uint32_t next_gen; + + next_gen = ++generation; + if (next_gen == 0) + next_gen = ++generation; + + p32->gtod_generation = 0; /* mark invalid, so commpage won't try to use it */ + p64->gtod_generation = 0; + + p32->gtod_ns_base = abstime; + p64->gtod_ns_base = abstime; + + p32->gtod_sec_base = secs; + p64->gtod_sec_base = secs; + + p32->gtod_generation = next_gen; /* mark data as valid */ + p64->gtod_generation = next_gen; +} + + +/* Update _COMM_PAGE_MEMORY_PRESSURE. Called periodically from vm's compute_memory_pressure() */ + +void +commpage_set_memory_pressure( + unsigned int pressure ) +{ + char *cp; + uint32_t *ip; + + cp = commPagePtr32; + if ( cp ) { + cp += (_COMM_PAGE_MEMORY_PRESSURE - _COMM_PAGE32_BASE_ADDRESS); + ip = (uint32_t*) cp; + *ip = (uint32_t) pressure; + } + + cp = commPagePtr64; + if ( cp ) { + cp += (_COMM_PAGE_MEMORY_PRESSURE - _COMM_PAGE32_START_ADDRESS); + ip = (uint32_t*) cp; + *ip = (uint32_t) pressure; + } + +} + + +/* Update _COMM_PAGE_SPIN_COUNT. We might want to reduce when running on a battery, etc. */ + +void +commpage_set_spin_count( + unsigned int count ) +{ + char *cp; + uint32_t *ip; + + if (count == 0) /* we test for 0 after decrement, not before */ + count = 1; + + cp = commPagePtr32; + if ( cp ) { + cp += (_COMM_PAGE_SPIN_COUNT - _COMM_PAGE32_BASE_ADDRESS); + ip = (uint32_t*) cp; + *ip = (uint32_t) count; + } + + cp = commPagePtr64; + if ( cp ) { + cp += (_COMM_PAGE_SPIN_COUNT - _COMM_PAGE32_START_ADDRESS); + ip = (uint32_t*) cp; + *ip = (uint32_t) count; + } + +} + + +/* Check to see if a given address is in the Preemption Free Zone (PFZ) */ + +uint32_t +commpage_is_in_pfz32(uint32_t addr32) +{ + if ( (addr32 >= _COMM_PAGE_PFZ_START) && (addr32 < _COMM_PAGE_PFZ_END)) { + return 1; + } + else + return 0; +} + +uint32_t +commpage_is_in_pfz64(addr64_t addr64) +{ + if ( (addr64 >= _COMM_PAGE_32_TO_64(_COMM_PAGE_PFZ_START)) + && (addr64 < _COMM_PAGE_32_TO_64(_COMM_PAGE_PFZ_END))) { + return 1; + } + else + return 0; +} + diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/commpage_asm.s xnu-1486.2.11/osfmk/i386/commpage/commpage_asm.s --- xnu-1486.2.11.orig/osfmk/i386/commpage/commpage_asm.s 2009-11-12 13:00:01.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/commpage_asm.s 2009-11-16 22:57:59.000000000 -0500 @@ -181,6 +181,7 @@ COMMPAGE_DESCRIPTOR_REFERENCE(bit_test_and_clear_up_64) COMMPAGE_DESCRIPTOR_REFERENCE(bzero_sse2_64) COMMPAGE_DESCRIPTOR_REFERENCE(bzero_sse42_64) + COMMPAGE_DESCRIPTOR_REFERENCE(bcopy_sse3_64) COMMPAGE_DESCRIPTOR_REFERENCE(bcopy_sse3x_64) COMMPAGE_DESCRIPTOR_REFERENCE(bcopy_sse42_64) COMMPAGE_DESCRIPTOR_REFERENCE(memset_pattern_sse2_64) diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/commpage_mach_absolute_time.s xnu-1486.2.11/osfmk/i386/commpage/commpage_mach_absolute_time.s --- xnu-1486.2.11.orig/osfmk/i386/commpage/commpage_mach_absolute_time.s 2009-11-12 13:00:01.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/commpage_mach_absolute_time.s 2009-11-16 22:57:59.000000000 -0500 @@ -30,6 +30,7 @@ #include #include #include +#include // mercurysquad: required for RTCLOCK_SCALE_UP_BY #include @@ -58,6 +59,16 @@ subl _COMM_PAGE_NT_TSC_BASE,%eax sbbl _COMM_PAGE_NT_TSC_BASE+4,%edx + + /* mercurysquad: Use scaled up tsc (refer to rtclock.c) */ + shll $ RTCLOCK_SCALE_UP_BITS, %edx + roll $ RTCLOCK_SCALE_UP_BITS, %eax + movl %eax,%ecx + andl $ RTCLOCK_SCALE_UP_MASK, %ecx + addl %ecx,%edx + notl %ecx + andl %ecx,%eax + movl _COMM_PAGE_NT_SCALE,%ecx @@ -161,6 +172,7 @@ shlq $32,%rdx // rax := ((edx << 32) | eax), ie 64-bit tsc orq %rdx,%rax subq _NT_TSC_BASE(%rsi), %rax // rax := (tsc - base_tsc) + shlq $ RTCLOCK_SCALE_UP_BITS, %rax // mercurysquad: scale up movl _NT_SCALE(%rsi),%ecx mulq %rcx // rdx:rax := (tsc - base_tsc) * scale shrdq $32,%rdx,%rax // _COMM_PAGE_NT_SHIFT is always 32 diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/sse3emu.c xnu-1486.2.11/osfmk/i386/commpage/sse3emu.c --- xnu-1486.2.11.orig/osfmk/i386/commpage/sse3emu.c 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/sse3emu.c 2009-11-16 22:57:59.000000000 -0500 @@ -0,0 +1,349 @@ +#include + +/* kaitek: the SSE3 emulator is automatically disabled if sse3emu_size is zero. */ + +const uint8_t sse3emu_data[] = { + 0x50, 0x53, 0x51, 0x89, 0xe3, 0x8b, 0x4b, 0x18, 0x89, 0xcc, 0xff, 0x73, + 0x0c, 0x51, 0xff, 0x73, 0x14, 0x6a, 0x00, 0xff, 0x73, 0x0c, 0x89, 0xdc, + 0x83, 0x6c, 0x24, 0x18, 0x14, 0xc7, 0x44, 0x24, 0x0c, 0x80, 0x41, 0xff, + 0xff, 0x59, 0x5b, 0x58, 0xcf, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x50, 0x53, 0x51, 0x8b, 0x4c, 0x24, 0x0c, 0x8b, 0x41, 0x00, 0x25, 0xff, + 0xff, 0xff, 0x00, 0x3d, 0xf2, 0x0f, 0xf0, 0x00, 0x75, 0x2c, 0x8b, 0x59, + 0x00, 0x81, 0xe3, 0x00, 0x00, 0x00, 0xff, 0x81, 0xcb, 0xf3, 0x0f, 0x6f, + 0x00, 0x0f, 0x20, 0xc0, 0x25, 0xff, 0xff, 0xfe, 0xff, 0x0f, 0x22, 0xc0, + 0x89, 0x59, 0x00, 0x0f, 0x20, 0xc0, 0x0d, 0x00, 0x00, 0x01, 0x00, 0x0f, + 0x22, 0xc0, 0x59, 0x5b, 0x58, 0xcf, 0x89, 0xe3, 0x8b, 0x4b, 0x18, 0x89, + 0xcc, 0xff, 0x73, 0x0c, 0x51, 0xff, 0x73, 0x14, 0x6a, 0x00, 0xff, 0x73, + 0x0c, 0x89, 0xdc, 0x83, 0x6c, 0x24, 0x18, 0x14, 0xc7, 0x44, 0x24, 0x0c, + 0x80, 0x41, 0xff, 0xff, 0x59, 0x5b, 0x58, 0xcf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x0a, 0x54, 0x75, 0x72, 0x62, 0x6f, 0x27, 0x73, 0x20, 0x53, 0x53, 0x45, + 0x33, 0x2d, 0x3e, 0x53, 0x53, 0x45, 0x32, 0x20, 0x45, 0x6d, 0x75, 0x6c, + 0x61, 0x74, 0x6f, 0x72, 0x20, 0x76, 0x32, 0x30, 0x30, 0x38, 0x30, 0x39, + 0x31, 0x37, 0x52, 0x43, 0x36, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20, 0x63, + 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x28, 0x43, 0x29, 0x43, 0x6f, + 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, 0x30, 0x38, + 0x20, 0x54, 0x75, 0x72, 0x62, 0x6f, 0x20, 0x28, 0x4d, 0x69, 0x6b, 0x65, + 0x20, 0x42, 0x79, 0x72, 0x6e, 0x65, 0x29, 0x0a, 0x41, 0x6c, 0x6c, 0x20, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x64, 0x2e, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, + 0x20, 0x74, 0x75, 0x72, 0x62, 0x6f, 0x40, 0x30, 0x78, 0x66, 0x65, 0x65, + 0x64, 0x62, 0x65, 0x65, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x69, 0x6e, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x6a, 0x00, 0x6a, 0x06, 0x60, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, + 0x00, 0x89, 0xe7, 0x89, 0xe3, 0x83, 0xeb, 0x48, 0x89, 0xdc, 0x83, 0xe4, + 0xf0, 0x89, 0x7c, 0x24, 0x00, 0xb8, 0x00, 0x42, 0xff, 0xff, 0xff, 0xd0, + 0x89, 0xfc, 0x83, 0xc4, 0x10, 0x83, 0xf8, 0x05, 0x74, 0x16, 0x61, 0x83, + 0xc4, 0x08, 0x50, 0x8b, 0x44, 0x24, 0x04, 0x89, 0x44, 0x24, 0x14, 0x58, + 0x83, 0xc4, 0x08, 0x9d, 0x83, 0xc4, 0x04, 0xc3, 0x61, 0x83, 0xc4, 0x08, + 0x8b, 0x5c, 0x24, 0x00, 0x8b, 0x43, 0x00, 0x83, 0xc4, 0x08, 0x9d, 0xcc, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x55, 0x89, 0xe5, 0x57, + 0x56, 0x53, 0x81, 0xec, 0x9c, 0x00, 0x00, 0x00, 0x8b, 0x45, 0x08, 0x8d, + 0x50, 0x10, 0x89, 0x95, 0x74, 0xff, 0xff, 0xff, 0x8b, 0x50, 0x44, 0x89, + 0x50, 0x1c, 0x8b, 0x75, 0x08, 0x8b, 0x56, 0x38, 0x8b, 0x1a, 0x0f, 0xb6, + 0xfb, 0x81, 0xff, 0xdd, 0x00, 0x00, 0x00, 0x74, 0x14, 0x81, 0xff, 0xdb, + 0x00, 0x00, 0x00, 0x74, 0x0c, 0x81, 0xff, 0xdf, 0x00, 0x00, 0x00, 0x0f, + 0x85, 0xcb, 0x01, 0x00, 0x00, 0x8d, 0x4a, 0x01, 0x89, 0x4d, 0x84, 0x0f, + 0xb6, 0x42, 0x01, 0x89, 0xc3, 0xc0, 0xeb, 0x06, 0x89, 0xc1, 0x80, 0xe1, + 0x07, 0x88, 0x8d, 0x58, 0xff, 0xff, 0xff, 0x89, 0xc1, 0x83, 0xe1, 0x38, + 0xc1, 0xe9, 0x03, 0x89, 0x8d, 0x7c, 0xff, 0xff, 0xff, 0x80, 0xfb, 0x03, + 0x75, 0x34, 0x0f, 0xb6, 0x85, 0x58, 0xff, 0xff, 0xff, 0x89, 0x45, 0xe0, + 0xc7, 0x45, 0xd8, 0x01, 0x00, 0x00, 0x00, 0xc7, 0x45, 0xe4, 0x00, 0x00, + 0x00, 0x00, 0x89, 0x4d, 0xdc, 0xc7, 0x45, 0xc8, 0x01, 0x00, 0x00, 0x00, + 0x89, 0x4d, 0xcc, 0x89, 0x45, 0xd0, 0xc7, 0x45, 0xd4, 0x00, 0x00, 0x00, + 0x00, 0xe9, 0xf3, 0x00, 0x00, 0x00, 0x80, 0xbd, 0x58, 0xff, 0xff, 0xff, + 0x04, 0x75, 0x75, 0x8d, 0x42, 0x02, 0x89, 0x45, 0x84, 0x0f, 0xb6, 0x42, + 0x02, 0x89, 0xc2, 0xc0, 0xea, 0x06, 0x88, 0x95, 0x7a, 0xff, 0xff, 0xff, + 0x89, 0xc2, 0x83, 0xe2, 0x38, 0xc1, 0xea, 0x03, 0x24, 0x07, 0x88, 0x85, + 0x58, 0xff, 0xff, 0xff, 0x80, 0xfa, 0x04, 0x75, 0x04, 0x31, 0xd2, 0xeb, + 0x17, 0xf7, 0xd2, 0x83, 0xe2, 0x07, 0x8b, 0x8d, 0x74, 0xff, 0xff, 0xff, + 0x8b, 0x14, 0x91, 0x0f, 0xb6, 0x8d, 0x7a, 0xff, 0xff, 0xff, 0xd3, 0xe2, + 0x80, 0xbd, 0x58, 0xff, 0xff, 0xff, 0x05, 0x75, 0x0b, 0x84, 0xdb, 0x75, + 0x07, 0xb9, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x6b, 0x0f, 0xb6, 0x85, 0x58, + 0xff, 0xff, 0xff, 0xf7, 0xd0, 0x83, 0xe0, 0x07, 0x8b, 0x8d, 0x74, 0xff, + 0xff, 0xff, 0x03, 0x14, 0x81, 0xb9, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x2e, + 0x80, 0xbd, 0x58, 0xff, 0xff, 0xff, 0x05, 0x75, 0x0e, 0x84, 0xdb, 0x75, + 0x0a, 0x8b, 0x52, 0x02, 0xb9, 0x04, 0x00, 0x00, 0x00, 0xeb, 0x38, 0x0f, + 0xb6, 0x85, 0x58, 0xff, 0xff, 0xff, 0xf7, 0xd0, 0x83, 0xe0, 0x07, 0x8b, + 0x8d, 0x74, 0xff, 0xff, 0xff, 0x8b, 0x14, 0x81, 0x31, 0xc9, 0x80, 0xfb, + 0x01, 0x75, 0x0e, 0x8b, 0x5d, 0x84, 0x0f, 0xbe, 0x43, 0x01, 0x01, 0xc2, + 0x83, 0xc1, 0x01, 0xeb, 0x0e, 0x80, 0xfb, 0x02, 0x75, 0x09, 0x8b, 0x45, + 0x84, 0x03, 0x50, 0x01, 0x83, 0xc1, 0x04, 0x89, 0x55, 0xe0, 0xc7, 0x45, + 0xd8, 0x00, 0x00, 0x00, 0x00, 0x89, 0x4d, 0xe4, 0x8b, 0x9d, 0x7c, 0xff, + 0xff, 0xff, 0x89, 0x5d, 0xdc, 0xc7, 0x45, 0xc8, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x5d, 0xcc, 0x89, 0x55, 0xd0, 0x89, 0x4d, 0xd4, 0x8b, 0x45, 0xd4, + 0x83, 0xc0, 0x02, 0x89, 0x85, 0x70, 0xff, 0xff, 0xff, 0xd9, 0x7d, 0xb8, + 0x0f, 0xb6, 0x45, 0xb8, 0x0d, 0x00, 0x0c, 0x00, 0x00, 0x89, 0x45, 0xc0, + 0x81, 0xff, 0xdd, 0x00, 0x00, 0x00, 0x75, 0x15, 0xdf, 0x7d, 0x98, 0x8d, + 0x4d, 0x98, 0x8b, 0x55, 0xd0, 0x8b, 0x01, 0x89, 0x02, 0x8b, 0x41, 0x04, + 0x89, 0x42, 0x04, 0xeb, 0x37, 0x81, 0xff, 0xdb, 0x00, 0x00, 0x00, 0x75, + 0x13, 0xdb, 0x5d, 0x98, 0xb9, 0x04, 0x00, 0x00, 0x00, 0x8d, 0x75, 0x98, + 0x8b, 0x7d, 0xd0, 0xfc, 0xf3, 0xa4, 0xeb, 0x19, 0x81, 0xff, 0xdf, 0x00, + 0x00, 0x00, 0x75, 0x14, 0xdf, 0x5d, 0x98, 0xb9, 0x02, 0x00, 0x00, 0x00, + 0x8d, 0x75, 0x98, 0x8b, 0x7d, 0xd0, 0xfc, 0xf3, 0xa4, 0x8b, 0x75, 0x08, + 0xd9, 0x6d, 0xb8, 0xe9, 0x89, 0x06, 0x00, 0x00, 0x66, 0x83, 0xfb, 0xff, + 0x75, 0x15, 0x8d, 0x55, 0x08, 0x89, 0x56, 0x2c, 0xc7, 0x85, 0x70, 0xff, + 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0xe9, 0x6b, 0x06, 0x00, 0x00, 0x8d, + 0x4d, 0x88, 0x66, 0x0f, 0x7f, 0x41, 0x20, 0x66, 0x0f, 0x7f, 0x49, 0x30, + 0x8d, 0x72, 0x03, 0x89, 0x75, 0x80, 0x0f, 0xb6, 0x42, 0x03, 0x89, 0xc1, + 0xc0, 0xe9, 0x06, 0x89, 0xce, 0x89, 0xc1, 0x80, 0xe1, 0x07, 0xbf, 0x38, + 0x00, 0x00, 0x00, 0x21, 0xc7, 0xc1, 0xef, 0x03, 0x89, 0xf0, 0x3c, 0x03, + 0x75, 0x30, 0x0f, 0xb6, 0xc1, 0x89, 0x45, 0xe0, 0xc7, 0x45, 0xd8, 0x01, + 0x00, 0x00, 0x00, 0xc7, 0x45, 0xe4, 0x00, 0x00, 0x00, 0x00, 0x89, 0x7d, + 0xdc, 0xc7, 0x45, 0xc8, 0x01, 0x00, 0x00, 0x00, 0x89, 0x7d, 0xcc, 0x89, + 0x45, 0xd0, 0xc7, 0x45, 0xd4, 0x00, 0x00, 0x00, 0x00, 0xe9, 0xe6, 0x00, + 0x00, 0x00, 0x80, 0xf9, 0x04, 0x75, 0x77, 0x8d, 0x4a, 0x04, 0x89, 0x4d, + 0x80, 0x0f, 0xb6, 0x42, 0x04, 0x89, 0xc2, 0xc0, 0xea, 0x06, 0x88, 0x95, + 0x7b, 0xff, 0xff, 0xff, 0x89, 0xc2, 0x83, 0xe2, 0x38, 0xc1, 0xea, 0x03, + 0x24, 0x07, 0x88, 0x85, 0x58, 0xff, 0xff, 0xff, 0x80, 0xfa, 0x04, 0x75, + 0x04, 0x31, 0xd2, 0xeb, 0x17, 0xf7, 0xd2, 0x83, 0xe2, 0x07, 0x8b, 0x8d, + 0x74, 0xff, 0xff, 0xff, 0x8b, 0x14, 0x91, 0x0f, 0xb6, 0x8d, 0x7b, 0xff, + 0xff, 0xff, 0xd3, 0xe2, 0x80, 0xbd, 0x58, 0xff, 0xff, 0xff, 0x05, 0x75, + 0x0d, 0x89, 0xf0, 0x84, 0xc0, 0x75, 0x07, 0xb9, 0x01, 0x00, 0x00, 0x00, + 0xeb, 0x66, 0x0f, 0xb6, 0x85, 0x58, 0xff, 0xff, 0xff, 0xf7, 0xd0, 0x83, + 0xe0, 0x07, 0x8b, 0x8d, 0x74, 0xff, 0xff, 0xff, 0x03, 0x14, 0x81, 0xb9, + 0x01, 0x00, 0x00, 0x00, 0xeb, 0x27, 0x80, 0xf9, 0x05, 0x75, 0x10, 0x89, + 0xf0, 0x84, 0xc0, 0x75, 0x0a, 0x8b, 0x52, 0x04, 0xb9, 0x04, 0x00, 0x00, + 0x00, 0xeb, 0x35, 0x89, 0xc8, 0xf7, 0xd0, 0x83, 0xe0, 0x07, 0x8b, 0x8d, + 0x74, 0xff, 0xff, 0xff, 0x8b, 0x14, 0x81, 0x31, 0xc9, 0x89, 0xf0, 0x2c, + 0x01, 0x75, 0x0e, 0x8b, 0x75, 0x80, 0x0f, 0xbe, 0x46, 0x01, 0x01, 0xc2, + 0x83, 0xc1, 0x01, 0xeb, 0x0f, 0x89, 0xf0, 0x3c, 0x02, 0x75, 0x09, 0x8b, + 0x75, 0x80, 0x03, 0x56, 0x01, 0x83, 0xc1, 0x04, 0x89, 0x55, 0xe0, 0xc7, + 0x45, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x89, 0x4d, 0xe4, 0x89, 0x7d, 0xdc, + 0xc7, 0x45, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x89, 0x7d, 0xcc, 0x89, 0x55, + 0xd0, 0x89, 0x4d, 0xd4, 0x8b, 0x45, 0xd4, 0x83, 0xc0, 0x04, 0x89, 0x85, + 0x70, 0xff, 0xff, 0xff, 0x8b, 0x45, 0xc8, 0x85, 0xc0, 0x74, 0x54, 0x8b, + 0x45, 0xd0, 0x83, 0xf8, 0x01, 0x74, 0x54, 0x85, 0xc0, 0x75, 0x06, 0x66, + 0x0f, 0x6f, 0xc8, 0xeb, 0x4a, 0x83, 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, + 0x6f, 0xca, 0xeb, 0x3f, 0x83, 0xf8, 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, + 0xcb, 0xeb, 0x34, 0x83, 0xf8, 0x04, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xcc, + 0xeb, 0x29, 0x83, 0xf8, 0x05, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xcd, 0xeb, + 0x1e, 0x83, 0xf8, 0x06, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xce, 0xeb, 0x13, + 0x83, 0xf8, 0x07, 0x75, 0x0e, 0x66, 0x0f, 0x6f, 0xcf, 0xeb, 0x08, 0x8b, + 0x45, 0xd0, 0xf3, 0x0f, 0x6f, 0x48, 0x00, 0x89, 0xd8, 0x25, 0xff, 0xff, + 0xff, 0x00, 0x3d, 0xf2, 0x0f, 0xf0, 0x00, 0x75, 0x09, 0xf3, 0x0f, 0x6f, + 0xc1, 0xe9, 0xe4, 0x03, 0x00, 0x00, 0x3d, 0xf2, 0x0f, 0x12, 0x00, 0x75, + 0x0a, 0x66, 0x0f, 0x70, 0xc1, 0x44, 0xe9, 0xd3, 0x03, 0x00, 0x00, 0x3d, + 0xf3, 0x0f, 0x16, 0x00, 0x0f, 0x85, 0xd5, 0x00, 0x00, 0x00, 0x66, 0x0f, + 0x70, 0xc1, 0xf5, 0x8b, 0x45, 0xcc, 0x85, 0xc0, 0x75, 0x0d, 0x8d, 0x55, + 0x88, 0x66, 0x0f, 0x6f, 0x4a, 0x30, 0xe9, 0x67, 0x04, 0x00, 0x00, 0x83, + 0xf8, 0x01, 0x75, 0x11, 0x8d, 0x4d, 0x88, 0x66, 0x0f, 0x6f, 0xc8, 0x66, + 0x0f, 0x6f, 0x41, 0x20, 0xe9, 0x51, 0x04, 0x00, 0x00, 0x83, 0xf8, 0x02, + 0x75, 0x16, 0x8d, 0x5d, 0x88, 0x66, 0x0f, 0x6f, 0xd0, 0x66, 0x0f, 0x6f, + 0x43, 0x20, 0x66, 0x0f, 0x6f, 0x4b, 0x30, 0xe9, 0x36, 0x04, 0x00, 0x00, + 0x83, 0xf8, 0x03, 0x75, 0x16, 0x8d, 0x75, 0x88, 0x66, 0x0f, 0x6f, 0xd8, + 0x66, 0x0f, 0x6f, 0x46, 0x20, 0x66, 0x0f, 0x6f, 0x4e, 0x30, 0xe9, 0x1b, + 0x04, 0x00, 0x00, 0x83, 0xf8, 0x04, 0x75, 0x16, 0x8d, 0x45, 0x88, 0x66, + 0x0f, 0x6f, 0xe0, 0x66, 0x0f, 0x6f, 0x40, 0x20, 0x66, 0x0f, 0x6f, 0x48, + 0x30, 0xe9, 0x00, 0x04, 0x00, 0x00, 0x83, 0xf8, 0x05, 0x75, 0x16, 0x8d, + 0x55, 0x88, 0x66, 0x0f, 0x6f, 0xe8, 0x66, 0x0f, 0x6f, 0x42, 0x20, 0x66, + 0x0f, 0x6f, 0x4a, 0x30, 0xe9, 0xe5, 0x03, 0x00, 0x00, 0x83, 0xf8, 0x06, + 0x75, 0x16, 0x8d, 0x4d, 0x88, 0x66, 0x0f, 0x6f, 0xf0, 0x66, 0x0f, 0x6f, + 0x41, 0x20, 0x66, 0x0f, 0x6f, 0x49, 0x30, 0xe9, 0xca, 0x03, 0x00, 0x00, + 0x83, 0xf8, 0x07, 0x0f, 0x85, 0xc1, 0x03, 0x00, 0x00, 0x8d, 0x5d, 0x88, + 0x66, 0x0f, 0x6f, 0xf8, 0x66, 0x0f, 0x6f, 0x43, 0x20, 0x66, 0x0f, 0x6f, + 0x4b, 0x30, 0xe9, 0xab, 0x03, 0x00, 0x00, 0x3d, 0xf3, 0x0f, 0x12, 0x00, + 0x75, 0x0a, 0x66, 0x0f, 0x70, 0xc1, 0xa0, 0xe9, 0xe2, 0x02, 0x00, 0x00, + 0x3d, 0xf2, 0x0f, 0x7c, 0x00, 0x75, 0x73, 0x8b, 0x45, 0xcc, 0x85, 0xc0, + 0x74, 0x4b, 0x83, 0xf8, 0x01, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc1, 0xeb, + 0x40, 0x83, 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc2, 0xeb, 0x35, + 0x83, 0xf8, 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc3, 0xeb, 0x2a, 0x83, + 0xf8, 0x04, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc4, 0xeb, 0x1f, 0x83, 0xf8, + 0x05, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc5, 0xeb, 0x14, 0x83, 0xf8, 0x06, + 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc6, 0xeb, 0x09, 0x83, 0xf8, 0x07, 0x75, + 0x04, 0x66, 0x0f, 0x6f, 0xc7, 0x8d, 0x75, 0x88, 0x66, 0x0f, 0x7f, 0x4e, + 0x00, 0x0f, 0x28, 0xc8, 0x89, 0xf0, 0x0f, 0xc6, 0x40, 0x00, 0x88, 0x89, + 0xf2, 0x0f, 0xc6, 0x4a, 0x00, 0xdd, 0x0f, 0x58, 0xc1, 0xe9, 0x68, 0x02, + 0x00, 0x00, 0x3d, 0xf2, 0x0f, 0x7d, 0x00, 0x75, 0x73, 0x8b, 0x45, 0xcc, + 0x85, 0xc0, 0x74, 0x4b, 0x83, 0xf8, 0x01, 0x75, 0x06, 0x66, 0x0f, 0x6f, + 0xc1, 0xeb, 0x40, 0x83, 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc2, + 0xeb, 0x35, 0x83, 0xf8, 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc3, 0xeb, + 0x2a, 0x83, 0xf8, 0x04, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc4, 0xeb, 0x1f, + 0x83, 0xf8, 0x05, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc5, 0xeb, 0x14, 0x83, + 0xf8, 0x06, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc6, 0xeb, 0x09, 0x83, 0xf8, + 0x07, 0x75, 0x04, 0x66, 0x0f, 0x6f, 0xc7, 0x8d, 0x4d, 0x88, 0x66, 0x0f, + 0x7f, 0x49, 0x00, 0x0f, 0x28, 0xc8, 0x89, 0xcb, 0x0f, 0xc6, 0x43, 0x00, + 0x88, 0x89, 0xce, 0x0f, 0xc6, 0x4e, 0x00, 0xdd, 0x0f, 0x5c, 0xc1, 0xe9, + 0xee, 0x01, 0x00, 0x00, 0x3d, 0x66, 0x0f, 0x7c, 0x00, 0x75, 0x77, 0x8b, + 0x45, 0xcc, 0x85, 0xc0, 0x74, 0x4b, 0x83, 0xf8, 0x01, 0x75, 0x06, 0x66, + 0x0f, 0x6f, 0xc1, 0xeb, 0x40, 0x83, 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, + 0x6f, 0xc2, 0xeb, 0x35, 0x83, 0xf8, 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, + 0xc3, 0xeb, 0x2a, 0x83, 0xf8, 0x04, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc4, + 0xeb, 0x1f, 0x83, 0xf8, 0x05, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc5, 0xeb, + 0x14, 0x83, 0xf8, 0x06, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc6, 0xeb, 0x09, + 0x83, 0xf8, 0x07, 0x75, 0x04, 0x66, 0x0f, 0x6f, 0xc7, 0x8d, 0x45, 0x88, + 0x66, 0x0f, 0x7f, 0x48, 0x00, 0x66, 0x0f, 0x28, 0xc8, 0x89, 0xc2, 0x66, + 0x0f, 0xc6, 0x42, 0x00, 0x00, 0x89, 0xc1, 0x66, 0x0f, 0xc6, 0x49, 0x00, + 0x03, 0x66, 0x0f, 0x58, 0xc1, 0xe9, 0x70, 0x01, 0x00, 0x00, 0x3d, 0x66, + 0x0f, 0x7d, 0x00, 0x75, 0x77, 0x8b, 0x45, 0xcc, 0x85, 0xc0, 0x74, 0x4b, + 0x83, 0xf8, 0x01, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc1, 0xeb, 0x40, 0x83, + 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc2, 0xeb, 0x35, 0x83, 0xf8, + 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc3, 0xeb, 0x2a, 0x83, 0xf8, 0x04, + 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc4, 0xeb, 0x1f, 0x83, 0xf8, 0x05, 0x75, + 0x06, 0x66, 0x0f, 0x6f, 0xc5, 0xeb, 0x14, 0x83, 0xf8, 0x06, 0x75, 0x06, + 0x66, 0x0f, 0x6f, 0xc6, 0xeb, 0x09, 0x83, 0xf8, 0x07, 0x75, 0x04, 0x66, + 0x0f, 0x6f, 0xc7, 0x8d, 0x5d, 0x88, 0x66, 0x0f, 0x7f, 0x4b, 0x00, 0x66, + 0x0f, 0x28, 0xc8, 0x89, 0xde, 0x66, 0x0f, 0xc6, 0x46, 0x00, 0x00, 0x89, + 0xd8, 0x66, 0x0f, 0xc6, 0x48, 0x00, 0x03, 0x66, 0x0f, 0x5c, 0xc1, 0xe9, + 0xf2, 0x00, 0x00, 0x00, 0x3d, 0xf2, 0x0f, 0xd0, 0x00, 0x75, 0x70, 0x8b, + 0x45, 0xcc, 0x85, 0xc0, 0x74, 0x4b, 0x83, 0xf8, 0x01, 0x75, 0x06, 0x66, + 0x0f, 0x6f, 0xc1, 0xeb, 0x40, 0x83, 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, + 0x6f, 0xc2, 0xeb, 0x35, 0x83, 0xf8, 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, + 0xc3, 0xeb, 0x2a, 0x83, 0xf8, 0x04, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc4, + 0xeb, 0x1f, 0x83, 0xf8, 0x05, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc5, 0xeb, + 0x14, 0x83, 0xf8, 0x06, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc6, 0xeb, 0x09, + 0x83, 0xf8, 0x07, 0x75, 0x04, 0x66, 0x0f, 0x6f, 0xc7, 0x8d, 0x55, 0x88, + 0x66, 0x0f, 0x7f, 0x42, 0x10, 0x0f, 0x5c, 0xc1, 0x0f, 0xc6, 0xc0, 0x88, + 0x89, 0xd1, 0x0f, 0x58, 0x49, 0x10, 0x0f, 0xc6, 0xc9, 0xdd, 0x0f, 0x14, + 0xc1, 0xeb, 0x7b, 0x3d, 0x66, 0x0f, 0xd0, 0x00, 0x74, 0x0a, 0xb8, 0x05, + 0x00, 0x00, 0x00, 0xe9, 0x30, 0x01, 0x00, 0x00, 0x8b, 0x45, 0xcc, 0x85, + 0xc0, 0x74, 0x4b, 0x83, 0xf8, 0x01, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc1, + 0xeb, 0x40, 0x83, 0xf8, 0x02, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc2, 0xeb, + 0x35, 0x83, 0xf8, 0x03, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc3, 0xeb, 0x2a, + 0x83, 0xf8, 0x04, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc4, 0xeb, 0x1f, 0x83, + 0xf8, 0x05, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc5, 0xeb, 0x14, 0x83, 0xf8, + 0x06, 0x75, 0x06, 0x66, 0x0f, 0x6f, 0xc6, 0xeb, 0x09, 0x83, 0xf8, 0x07, + 0x75, 0x04, 0x66, 0x0f, 0x6f, 0xc7, 0x8d, 0x5d, 0x88, 0x66, 0x0f, 0x7f, + 0x43, 0x10, 0x66, 0x0f, 0x5c, 0xc1, 0x89, 0xde, 0x66, 0x0f, 0x58, 0x4e, + 0x10, 0x66, 0x0f, 0xc6, 0xc1, 0x03, 0x8b, 0x45, 0xcc, 0x85, 0xc0, 0x75, + 0x0d, 0x8d, 0x45, 0x88, 0x66, 0x0f, 0x6f, 0x48, 0x30, 0xe9, 0xa4, 0x00, + 0x00, 0x00, 0x83, 0xf8, 0x01, 0x75, 0x11, 0x8d, 0x55, 0x88, 0x66, 0x0f, + 0x6f, 0xc8, 0x66, 0x0f, 0x6f, 0x42, 0x20, 0xe9, 0x8e, 0x00, 0x00, 0x00, + 0x83, 0xf8, 0x02, 0x75, 0x13, 0x8d, 0x4d, 0x88, 0x66, 0x0f, 0x6f, 0xd0, + 0x66, 0x0f, 0x6f, 0x41, 0x20, 0x66, 0x0f, 0x6f, 0x49, 0x30, 0xeb, 0x76, + 0x83, 0xf8, 0x03, 0x75, 0x13, 0x8d, 0x5d, 0x88, 0x66, 0x0f, 0x6f, 0xd8, + 0x66, 0x0f, 0x6f, 0x43, 0x20, 0x66, 0x0f, 0x6f, 0x4b, 0x30, 0xeb, 0x5e, + 0x83, 0xf8, 0x04, 0x75, 0x13, 0x8d, 0x75, 0x88, 0x66, 0x0f, 0x6f, 0xe0, + 0x66, 0x0f, 0x6f, 0x46, 0x20, 0x66, 0x0f, 0x6f, 0x4e, 0x30, 0xeb, 0x46, + 0x83, 0xf8, 0x05, 0x75, 0x13, 0x8d, 0x45, 0x88, 0x66, 0x0f, 0x6f, 0xe8, + 0x66, 0x0f, 0x6f, 0x40, 0x20, 0x66, 0x0f, 0x6f, 0x48, 0x30, 0xeb, 0x2e, + 0x83, 0xf8, 0x06, 0x75, 0x13, 0x8d, 0x55, 0x88, 0x66, 0x0f, 0x6f, 0xf0, + 0x66, 0x0f, 0x6f, 0x42, 0x20, 0x66, 0x0f, 0x6f, 0x4a, 0x30, 0xeb, 0x16, + 0x83, 0xf8, 0x07, 0x75, 0x11, 0x8d, 0x4d, 0x88, 0x66, 0x0f, 0x6f, 0xf8, + 0x66, 0x0f, 0x6f, 0x41, 0x20, 0x66, 0x0f, 0x6f, 0x49, 0x30, 0x8b, 0x75, + 0x08, 0x8b, 0x9d, 0x70, 0xff, 0xff, 0xff, 0x01, 0x5e, 0x38, 0x31, 0xc0, + 0x81, 0xc4, 0x9c, 0x00, 0x00, 0x00, 0x5b, 0x5e, 0x5f, 0x5d, 0xc3, 0x00, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90 +}; +const uint32_t sse3emu_size = 4096; diff -Naur xnu-1486.2.11.orig/osfmk/i386/commpage/sse3emu.h xnu-1486.2.11/osfmk/i386/commpage/sse3emu.h --- xnu-1486.2.11.orig/osfmk/i386/commpage/sse3emu.h 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/commpage/sse3emu.h 2009-11-16 22:57:59.000000000 -0500 @@ -0,0 +1,11 @@ +#ifndef _SSE3EMU_H +#define _SSE3EMU_H + +#include + +/* mercurysquad / turbo: Voodoo XNU SSE3 emulator. */ + +extern const uint32_t sse3emu_size; +extern const uint8_t sse3emu_data[]; + +#endif \ No newline at end of file diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpu_capabilities.h xnu-1486.2.11/osfmk/i386/cpu_capabilities.h --- xnu-1486.2.11.orig/osfmk/i386/cpu_capabilities.h 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpu_capabilities.h 2009-11-16 22:57:59.000000000 -0500 @@ -250,6 +250,8 @@ #define _COMM_PAGE_END (_COMM_PAGE_START_ADDRESS+0x1fff) /* end of common page - insert new stuff here */ +#define _COMM_PAGE_SSE3EMU (_COMM_PAGE_START_ADDRESS+0x4000) + /* _COMM_PAGE_COMPARE_AND_SWAP{32,64}B are not used on x86 and are * maintained here for source compatability. These will be removed at * some point, so don't go relying on them. */ diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpu_capabilities.h.orig xnu-1486.2.11/osfmk/i386/cpu_capabilities.h.orig --- xnu-1486.2.11.orig/osfmk/i386/cpu_capabilities.h.orig 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpu_capabilities.h.orig 2009-11-16 22:29:44.000000000 -0500 @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2003-2009 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@ + */ +#ifdef PRIVATE + +#ifndef _I386_CPU_CAPABILITIES_H +#define _I386_CPU_CAPABILITIES_H + +#ifndef __ASSEMBLER__ +#include +#endif + +/* + * This API only supported for Apple internal use. + */ + +/* Bit definitions for _cpu_capabilities: */ + +#define kHasMMX 0x00000001 +#define kHasSSE 0x00000002 +#define kHasSSE2 0x00000004 +#define kHasSSE3 0x00000008 +#define kCache32 0x00000010 /* cache line size is 32 bytes */ +#define kCache64 0x00000020 +#define kCache128 0x00000040 +#define kFastThreadLocalStorage 0x00000080 /* TLS ptr is kept in a user-mode-readable register */ +#define kHasSupplementalSSE3 0x00000100 +#define k64Bit 0x00000200 /* processor supports EM64T (not what mode you're running in) */ +#define kHasSSE4_1 0x00000400 +#define kHasSSE4_2 0x00000800 +#define kHasAES 0x00001000 +#define kInOrderPipeline 0x00002000 /* in-order execution */ +#define kSlow 0x00004000 /* tsc < nanosecond */ +#define kUP 0x00008000 /* set if (kNumCPUs == 1) */ +#define kNumCPUs 0x00FF0000 /* number of CPUs (see _NumCPUs() below) */ + +#define kNumCPUsShift 16 /* see _NumCPUs() below */ + +#ifndef __ASSEMBLER__ +#include + +__BEGIN_DECLS +extern int _get_cpu_capabilities( void ); +__END_DECLS + +inline static +int _NumCPUs( void ) +{ + return (_get_cpu_capabilities() & kNumCPUs) >> kNumCPUsShift; +} + +#endif /* __ASSEMBLER__ */ + + +/* + * The shared kernel/user "comm page(s)": + * + * The last several pages of every address space are reserved for the kernel/user + * "comm area". During system initialization, the kernel populates the comm pages with + * code customized for the particular processor and platform. + * + * Because Mach VM cannot map the last page of an address space, we don't use it. + */ + +#define _COMM_PAGE32_AREA_LENGTH ( 19 * 4096 ) /* reserved length of entire comm area */ +#define _COMM_PAGE32_BASE_ADDRESS ( 0xfffec000 ) /* base address of allocated memory, -20 pages */ +#define _COMM_PAGE32_START_ADDRESS ( 0xffff0000 ) /* address traditional commpage code starts on, -16 pages */ +#define _COMM_PAGE32_AREA_USED ( 19 * 4096 ) /* this is the amt actually allocated */ +#define _COMM_PAGE32_SIGS_OFFSET 0x8000 /* offset to routine signatures */ + +#define _COMM_PAGE64_AREA_LENGTH ( 2 * 1024 * 1024 ) /* reserved length of entire comm area (2MB) */ +#define _COMM_PAGE64_BASE_ADDRESS ( 0x00007fffffe00000ULL ) /* base address of allocated memory */ +#define _COMM_PAGE64_START_ADDRESS ( _COMM_PAGE64_BASE_ADDRESS ) /* address traditional commpage code starts on */ +#define _COMM_PAGE64_AREA_USED ( 2 * 4096 ) /* this is the amt actually populated */ + +/* no need for an Objective-C area on Intel */ +#define _COMM_PAGE32_OBJC_SIZE 0ULL +#define _COMM_PAGE32_OBJC_BASE 0ULL +#define _COMM_PAGE64_OBJC_SIZE 0ULL +#define _COMM_PAGE64_OBJC_BASE 0ULL + +#ifdef KERNEL_PRIVATE + +/* Inside the kernel, comm page addresses are absolute addresses + * assuming they are a part of the 32-bit commpage. They may + * be mapped somewhere else, especially for the 64-bit commpage. + */ +#define _COMM_PAGE_START_ADDRESS _COMM_PAGE32_START_ADDRESS +#define _COMM_PAGE_SIGS_OFFSET _COMM_PAGE32_SIGS_OFFSET + +#else /* !KERNEL_PRIVATE */ + +#if defined(__i386__) + +#define _COMM_PAGE_AREA_LENGTH _COMM_PAGE32_AREA_LENGTH +#define _COMM_PAGE_BASE_ADDRESS _COMM_PAGE32_BASE_ADDRESS +#define _COMM_PAGE_START_ADDRESS _COMM_PAGE32_START_ADDRESS +#define _COMM_PAGE_AREA_USED _COMM_PAGE32_AREA_USED +#define _COMM_PAGE_SIGS_OFFSET _COMM_PAGE32_SIGS_OFFSET + +#elif defined(__x86_64__) + +#define _COMM_PAGE_AREA_LENGTH _COMM_PAGE64_AREA_LENGTH +#define _COMM_PAGE_BASE_ADDRESS _COMM_PAGE64_BASE_ADDRESS +#define _COMM_PAGE_START_ADDRESS _COMM_PAGE64_START_ADDRESS +#define _COMM_PAGE_AREA_USED _COMM_PAGE64_AREA_USED + +#else +#error architecture not supported +#endif + +#endif /* !KERNEL_PRIVATE */ + +/* data in the comm page */ + +#define _COMM_PAGE_SIGNATURE (_COMM_PAGE_START_ADDRESS+0x000) /* first few bytes are a signature */ +#define _COMM_PAGE_VERSION (_COMM_PAGE_START_ADDRESS+0x01E) /* 16-bit version# */ +#define _COMM_PAGE_THIS_VERSION 11 /* version of the commarea format */ + +#define _COMM_PAGE_CPU_CAPABILITIES (_COMM_PAGE_START_ADDRESS+0x020) /* uint32_t _cpu_capabilities */ +#define _COMM_PAGE_NCPUS (_COMM_PAGE_START_ADDRESS+0x022) /* uint8_t number of configured CPUs */ +#define _COMM_PAGE_CACHE_LINESIZE (_COMM_PAGE_START_ADDRESS+0x026) /* uint16_t cache line size */ + +#define _COMM_PAGE_SCHED_GEN (_COMM_PAGE_START_ADDRESS+0x028) /* uint32_t scheduler generation number (count of pre-emptions) */ +#define _COMM_PAGE_MEMORY_PRESSURE (_COMM_PAGE_START_ADDRESS+0x02c) /* uint32_t copy of vm_memory_pressure */ +#define _COMM_PAGE_SPIN_COUNT (_COMM_PAGE_START_ADDRESS+0x030) /* uint32_t max spin count for mutex's */ + +#define _COMM_PAGE_UNUSED1 (_COMM_PAGE_START_ADDRESS+0x034) /* 12 unused bytes */ + +#ifdef KERNEL_PRIVATE + +/* slots defined in all cases, but commpage setup code must not populate for 64-bit commpage */ +#define _COMM_PAGE_2_TO_52 (_COMM_PAGE_START_ADDRESS+0x040) /* double float constant 2**52 */ +#define _COMM_PAGE_10_TO_6 (_COMM_PAGE_START_ADDRESS+0x048) /* double float constant 10**6 */ + +#else /* !KERNEL_PRIVATE */ + +#if defined(__i386__) /* following are not defined in 64-bit */ +#define _COMM_PAGE_2_TO_52 (_COMM_PAGE_START_ADDRESS+0x040) /* double float constant 2**52 */ +#define _COMM_PAGE_10_TO_6 (_COMM_PAGE_START_ADDRESS+0x048) /* double float constant 10**6 */ +#else +#define _COMM_PAGE_UNUSED2 (_COMM_PAGE_START_ADDRESS+0x040) /* 16 unused bytes */ +#endif + +#endif /* !KERNEL_PRIVATE */ + +#define _COMM_PAGE_TIME_DATA_START (_COMM_PAGE_START_ADDRESS+0x050) /* base of offsets below (_NT_SCALE etc) */ +#define _COMM_PAGE_NT_TSC_BASE (_COMM_PAGE_START_ADDRESS+0x050) /* used by nanotime() */ +#define _COMM_PAGE_NT_SCALE (_COMM_PAGE_START_ADDRESS+0x058) /* used by nanotime() */ +#define _COMM_PAGE_NT_SHIFT (_COMM_PAGE_START_ADDRESS+0x05c) /* used by nanotime() */ +#define _COMM_PAGE_NT_NS_BASE (_COMM_PAGE_START_ADDRESS+0x060) /* used by nanotime() */ +#define _COMM_PAGE_NT_GENERATION (_COMM_PAGE_START_ADDRESS+0x068) /* used by nanotime() */ +#define _COMM_PAGE_GTOD_GENERATION (_COMM_PAGE_START_ADDRESS+0x06c) /* used by gettimeofday() */ +#define _COMM_PAGE_GTOD_NS_BASE (_COMM_PAGE_START_ADDRESS+0x070) /* used by gettimeofday() */ +#define _COMM_PAGE_GTOD_SEC_BASE (_COMM_PAGE_START_ADDRESS+0x078) /* used by gettimeofday() */ + +/* Warning: kernel commpage.h has a matching c typedef for the following. They must be kept in sync. */ +/* These offsets are from _COMM_PAGE_TIME_DATA_START */ + +#define _NT_TSC_BASE 0 +#define _NT_SCALE 8 +#define _NT_SHIFT 12 +#define _NT_NS_BASE 16 +#define _NT_GENERATION 24 +#define _GTOD_GENERATION 28 +#define _GTOD_NS_BASE 32 +#define _GTOD_SEC_BASE 40 + + /* jump table (jmp to this address, which may be a branch to the actual code somewhere else) */ + /* When new jump table entries are added, corresponding symbols should be added below */ + /* New slots should be allocated with at least 16-byte alignment. Some like bcopy require */ + /* 32-byte alignment, and should be aligned as such in the assembly source before they are relocated */ +#define _COMM_PAGE_COMPARE_AND_SWAP32 (_COMM_PAGE_START_ADDRESS+0x080) /* compare-and-swap word */ +#define _COMM_PAGE_COMPARE_AND_SWAP64 (_COMM_PAGE_START_ADDRESS+0x0c0) /* compare-and-swap doubleword */ +#define _COMM_PAGE_ENQUEUE (_COMM_PAGE_START_ADDRESS+0x100) /* enqueue */ +#define _COMM_PAGE_DEQUEUE (_COMM_PAGE_START_ADDRESS+0x140) /* dequeue */ +#define _COMM_PAGE_MEMORY_BARRIER (_COMM_PAGE_START_ADDRESS+0x180) /* memory barrier */ +#define _COMM_PAGE_ATOMIC_ADD32 (_COMM_PAGE_START_ADDRESS+0x1a0) /* add atomic word */ +#define _COMM_PAGE_ATOMIC_ADD64 (_COMM_PAGE_START_ADDRESS+0x1c0) /* add atomic doubleword */ + +#define _COMM_PAGE_CPU_NUMBER (_COMM_PAGE_START_ADDRESS+0x1e0) /* user-level cpu_number() */ + +#define _COMM_PAGE_ABSOLUTE_TIME (_COMM_PAGE_START_ADDRESS+0x200) /* mach_absolute_time() */ +#define _COMM_PAGE_SPINLOCK_TRY (_COMM_PAGE_START_ADDRESS+0x220) /* spinlock_try() */ +#define _COMM_PAGE_SPINLOCK_LOCK (_COMM_PAGE_START_ADDRESS+0x260) /* spinlock_lock() */ +#define _COMM_PAGE_SPINLOCK_UNLOCK (_COMM_PAGE_START_ADDRESS+0x2a0) /* spinlock_unlock() */ +#define _COMM_PAGE_PTHREAD_GETSPECIFIC (_COMM_PAGE_START_ADDRESS+0x2c0) /* pthread_getspecific() */ +#define _COMM_PAGE_GETTIMEOFDAY (_COMM_PAGE_START_ADDRESS+0x2e0) /* used by gettimeofday() */ +#define _COMM_PAGE_FLUSH_DCACHE (_COMM_PAGE_START_ADDRESS+0x4e0) /* sys_dcache_flush() */ +#define _COMM_PAGE_FLUSH_ICACHE (_COMM_PAGE_START_ADDRESS+0x520) /* sys_icache_invalidate() */ +#define _COMM_PAGE_PTHREAD_SELF (_COMM_PAGE_START_ADDRESS+0x580) /* pthread_self() */ + +#define _COMM_PAGE_PREEMPT (_COMM_PAGE_START_ADDRESS+0x5a0) /* used by PFZ code */ + +#define _COMM_PAGE_RELINQUISH (_COMM_PAGE_START_ADDRESS+0x5c0) /* used by spinlocks */ +#define _COMM_PAGE_BTS (_COMM_PAGE_START_ADDRESS+0x5e0) /* bit test-and-set */ +#define _COMM_PAGE_BTC (_COMM_PAGE_START_ADDRESS+0x5f0) /* bit test-and-clear */ + +#define _COMM_PAGE_BZERO (_COMM_PAGE_START_ADDRESS+0x600) /* bzero() */ +#define _COMM_PAGE_BCOPY (_COMM_PAGE_START_ADDRESS+0x780) /* bcopy() */ +#define _COMM_PAGE_MEMCPY (_COMM_PAGE_START_ADDRESS+0x7a0) /* memcpy() */ +#define _COMM_PAGE_MEMMOVE (_COMM_PAGE_START_ADDRESS+0x7a0) /* memmove() */ +#define _COMM_PAGE_BCOPY_END (_COMM_PAGE_START_ADDRESS+0xfff) /* used by rosetta */ + +#define _COMM_PAGE_MEMSET_PATTERN (_COMM_PAGE_START_ADDRESS+0x1000) /* used by nonzero memset() */ +#define _COMM_PAGE_LONGCOPY (_COMM_PAGE_START_ADDRESS+0x1200) /* used by bcopy() for very long operands */ +#define _COMM_PAGE_LONGCOPY_END (_COMM_PAGE_START_ADDRESS+0x15ff) /* used by rosetta */ + +#define _COMM_PAGE_BACKOFF (_COMM_PAGE_START_ADDRESS+0x1600) /* called from PFZ */ +#define _COMM_PAGE_FIFO_ENQUEUE (_COMM_PAGE_START_ADDRESS+0x1680) /* FIFO enqueue */ +#define _COMM_PAGE_FIFO_DEQUEUE (_COMM_PAGE_START_ADDRESS+0x16c0) /* FIFO dequeue */ +#define _COMM_PAGE_NANOTIME (_COMM_PAGE_START_ADDRESS+0x1700) /* nanotime() */ +#define _COMM_PAGE_MUTEX_LOCK (_COMM_PAGE_START_ADDRESS+0x1780) /* pthread_mutex_lock() */ + +#define _COMM_PAGE_UNUSED5 (_COMM_PAGE_START_ADDRESS+0x17e0) /* unused space for regular code up to 0x1c00 */ + +#define _COMM_PAGE_PFZ_START (_COMM_PAGE_START_ADDRESS+0x1c00) /* start of Preemption Free Zone */ + +#define _COMM_PAGE_PFZ_ENQUEUE (_COMM_PAGE_START_ADDRESS+0x1c00) /* internal routine for FIFO enqueue */ +#define _COMM_PAGE_PFZ_DEQUEUE (_COMM_PAGE_START_ADDRESS+0x1c80) /* internal routine for FIFO dequeue */ +#define _COMM_PAGE_PFZ_MUTEX_LOCK (_COMM_PAGE_START_ADDRESS+0x1d00) /* internal routine for pthread_mutex_lock() */ + +#define _COMM_PAGE_UNUSED6 (_COMM_PAGE_START_ADDRESS+0x1d80) /* unused space for PFZ code up to 0x1fff */ + +#define _COMM_PAGE_PFZ_END (_COMM_PAGE_START_ADDRESS+0x1fff) /* end of Preemption Free Zone */ + +#define _COMM_PAGE_END (_COMM_PAGE_START_ADDRESS+0x1fff) /* end of common page - insert new stuff here */ + +/* _COMM_PAGE_COMPARE_AND_SWAP{32,64}B are not used on x86 and are + * maintained here for source compatability. These will be removed at + * some point, so don't go relying on them. */ +#define _COMM_PAGE_COMPARE_AND_SWAP32B (_COMM_PAGE_START_ADDRESS+0xf80) /* compare-and-swap word w barrier */ +#define _COMM_PAGE_COMPARE_AND_SWAP64B (_COMM_PAGE_START_ADDRESS+0xfc0) /* compare-and-swap doubleword w barrier */ + +#ifdef __ASSEMBLER__ +#ifdef __COMM_PAGE_SYMBOLS + +#define CREATE_COMM_PAGE_SYMBOL(symbol_name, symbol_address) \ + .org (symbol_address - (_COMM_PAGE_START_ADDRESS & 0xFFFFE000)) ;\ +symbol_name: nop + + .text /* Required to make a well behaved symbol file */ + + CREATE_COMM_PAGE_SYMBOL(___compare_and_swap32, _COMM_PAGE_COMPARE_AND_SWAP32) + CREATE_COMM_PAGE_SYMBOL(___compare_and_swap64, _COMM_PAGE_COMPARE_AND_SWAP64) + CREATE_COMM_PAGE_SYMBOL(___atomic_enqueue, _COMM_PAGE_ENQUEUE) + CREATE_COMM_PAGE_SYMBOL(___atomic_dequeue, _COMM_PAGE_DEQUEUE) + CREATE_COMM_PAGE_SYMBOL(___memory_barrier, _COMM_PAGE_MEMORY_BARRIER) + CREATE_COMM_PAGE_SYMBOL(___atomic_add32, _COMM_PAGE_ATOMIC_ADD32) + CREATE_COMM_PAGE_SYMBOL(___atomic_add64, _COMM_PAGE_ATOMIC_ADD64) + CREATE_COMM_PAGE_SYMBOL(___cpu_number, _COMM_PAGE_CPU_NUMBER) + CREATE_COMM_PAGE_SYMBOL(___mach_absolute_time, _COMM_PAGE_ABSOLUTE_TIME) + CREATE_COMM_PAGE_SYMBOL(___spin_lock_try, _COMM_PAGE_SPINLOCK_TRY) + CREATE_COMM_PAGE_SYMBOL(___spin_lock, _COMM_PAGE_SPINLOCK_LOCK) + CREATE_COMM_PAGE_SYMBOL(___spin_unlock, _COMM_PAGE_SPINLOCK_UNLOCK) + CREATE_COMM_PAGE_SYMBOL(___pthread_getspecific, _COMM_PAGE_PTHREAD_GETSPECIFIC) + CREATE_COMM_PAGE_SYMBOL(___gettimeofday, _COMM_PAGE_GETTIMEOFDAY) + CREATE_COMM_PAGE_SYMBOL(___sys_dcache_flush, _COMM_PAGE_FLUSH_DCACHE) + CREATE_COMM_PAGE_SYMBOL(___sys_icache_invalidate, _COMM_PAGE_FLUSH_ICACHE) + CREATE_COMM_PAGE_SYMBOL(___pthread_self, _COMM_PAGE_PTHREAD_SELF) + CREATE_COMM_PAGE_SYMBOL(___pfz_preempt, _COMM_PAGE_PREEMPT) + CREATE_COMM_PAGE_SYMBOL(___spin_lock_relinquish, _COMM_PAGE_RELINQUISH) + CREATE_COMM_PAGE_SYMBOL(___bit_test_and_set, _COMM_PAGE_BTS) + CREATE_COMM_PAGE_SYMBOL(___bit_test_and_clear, _COMM_PAGE_BTC) + CREATE_COMM_PAGE_SYMBOL(___bzero, _COMM_PAGE_BZERO) + CREATE_COMM_PAGE_SYMBOL(___bcopy, _COMM_PAGE_BCOPY) + CREATE_COMM_PAGE_SYMBOL(___memcpy, _COMM_PAGE_MEMCPY) +/* CREATE_COMM_PAGE_SYMBOL(___memmove, _COMM_PAGE_MEMMOVE) */ + CREATE_COMM_PAGE_SYMBOL(___memset_pattern, _COMM_PAGE_MEMSET_PATTERN) + CREATE_COMM_PAGE_SYMBOL(___longcopy, _COMM_PAGE_LONGCOPY) + CREATE_COMM_PAGE_SYMBOL(___backoff, _COMM_PAGE_BACKOFF) + CREATE_COMM_PAGE_SYMBOL(___fifo_enqueue, _COMM_PAGE_FIFO_ENQUEUE) + CREATE_COMM_PAGE_SYMBOL(___fifo_dequeue, _COMM_PAGE_FIFO_DEQUEUE) + CREATE_COMM_PAGE_SYMBOL(___nanotime, _COMM_PAGE_NANOTIME) + CREATE_COMM_PAGE_SYMBOL(___mutex_lock, _COMM_PAGE_MUTEX_LOCK) + CREATE_COMM_PAGE_SYMBOL(___pfz_enqueue, _COMM_PAGE_PFZ_ENQUEUE) + CREATE_COMM_PAGE_SYMBOL(___pfz_dequeue, _COMM_PAGE_PFZ_DEQUEUE) + CREATE_COMM_PAGE_SYMBOL(___pfz_mutex_lock, _COMM_PAGE_PFZ_MUTEX_LOCK) + CREATE_COMM_PAGE_SYMBOL(___end_comm_page, _COMM_PAGE_END) + + .data /* Required to make a well behaved symbol file */ + .long 0 /* Required to make a well behaved symbol file */ + +#endif /* __COMM_PAGE_SYMBOLS */ +#endif /* __ASSEMBLER__ */ + +#endif /* _I386_CPU_CAPABILITIES_H */ +#endif /* PRIVATE */ diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpu_topology.c xnu-1486.2.11/osfmk/i386/cpu_topology.c --- xnu-1486.2.11.orig/osfmk/i386/cpu_topology.c 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpu_topology.c 2009-11-16 22:57:59.000000000 -0500 @@ -163,7 +163,7 @@ x86_affinity_set_t *aset; LLC_cachep = lcpup->caches[topoParms.LLCDepth]; - assert(LLC_cachep->type == CPU_CACHE_TYPE_UNIF); + // assert(LLC_cachep->type == CPU_CACHE_TYPE_UNIF); // turbo: this assertion causes null pointer deref aset = find_cache_affinity(LLC_cachep); if (aset == NULL) { aset = (x86_affinity_set_t *) kalloc(sizeof(*aset)); diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpuid.c xnu-1486.2.11/osfmk/i386/cpuid.c --- xnu-1486.2.11.orig/osfmk/i386/cpuid.c 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpuid.c 2009-11-17 00:17:09.000000000 -0500 @@ -32,8 +32,9 @@ #include #include #include - +#include #include +#include "cpuid_legacy.h" #if MACH_KDB #include #include @@ -246,7 +247,179 @@ } } #endif +boolean_t ForceAmdCpu = FALSE; + +/* Handy functions to check what platform we're on */ +boolean_t IsAmdCPU() { + if (ForceAmdCpu) return TRUE; + + uint32_t ourcpuid[4]; + do_cpuid(0, ourcpuid); + if (ourcpuid[ebx] == 0x68747541 && + ourcpuid[ecx] == 0x444D4163 && + ourcpuid[edx] == 0x69746E65) + return TRUE; + else + return FALSE; +}; + +boolean_t IsIntelCPU() { + return !IsAmdCPU(); // dirty hack +} + +/* mercurysquad: this function is AMD-specific */ +void +get_amd_cache_info( + i386_cpu_info_t* info_p, + uint32_t linesizes[], + cache_type_t type, + uint32_t* geometry_colors, + uint32_t n_cores) +{ + uint32_t reg[4] = {0, 0, 0, 0}; + uint32_t cache_level; + uint32_t cache_sharing; + uint32_t cache_linesize; + uint32_t cache_associativity; + uint32_t cache_size; + uint32_t cache_sets; + uint32_t cache_partitions; + uint32_t reg_to_use; + uint32_t colors; + uint32_t ncores; + + switch (type) { + case L1D: + cache_level = 1; + do_cpuid(0x80000005, reg); + reg_to_use = ecx; + break; + case L1I: + cache_level = 1; + do_cpuid(0x80000005, reg); + reg_to_use = edx; + break; + case L2U: + cache_level = 2; + do_cpuid(0x80000006, reg); + reg_to_use = ecx; + break; + case L3U: + cache_level = 3; + do_cpuid(0x80000006, reg); + reg_to_use = edx; + break; + case Lnone: + default: + return; + }; + + /* Avoid div0 errors if we couldn't get the # of cores */ + if (n_cores == 0) + ncores = 1; + else + ncores = n_cores; + + cache_size = bitfield32(reg[reg_to_use], 31, 16); + + if (cache_size == 0) { + /* Cache doesn't exist, set it as zero */ + info_p->cache_size[type] = 0; + info_p->cache_sharing[type] = 0; + info_p->cache_partitions[type] = 0; + return; + } + + switch (cache_level) { + case 1: + /* L1 uses only bits 31 to 24 so we should shift right 8 bits + * This is in KB units, and is reported per-core. + */ + cache_size = (cache_size >> 8) * 1024; + cache_sharing = 1; + break; + case 2: + /* L2 cache is in KB units, reported per-core */ + cache_size *= 1024; + cache_sharing = 1; + break; + case 3: + /* L3 is in 512 KB units. This is reported by CPU as total, and + * we export it as per-core. */ + cache_size = cache_size * 512 * 1024 / ncores; + cache_sharing = ncores; + break; + default: + BUG("invalid cache level"); + return; /* not reached, silences optimizer */ + }; + + cache_linesize = bitfield32(reg[reg_to_use], 7, 0); + cache_partitions = bitfield32(reg[reg_to_use], 15, 8); // Needs review + cache_associativity = bitfield32(reg[reg_to_use], 23, 16); + + /* For L2/L3 caches, AMD uses an encoding for associativity. + * The formula is 2 ^ (assoc / 2) + */ + if (cache_level > 1) { + cache_associativity = 1ul << (cache_associativity / 2); + } + + info_p->cache_size[type] = cache_size; + info_p->cache_sharing[type] = cache_sharing; + info_p->cache_partitions[type] = cache_partitions; + + if (type == L2U) { + info_p->cpuid_cache_L2_associativity = cache_associativity; + info_p->cpuid_cache_size = cache_size; + } + + linesizes[type] = cache_linesize; + cache_sets = cache_size / (cache_associativity * cache_linesize * cache_partitions); + colors = ( cache_linesize * cache_sets ) >> 12; + + if ( colors > *geometry_colors ) + *geometry_colors = colors; +} +static void +cpuid_set_amd_cache_info( i386_cpu_info_t * info_p ) +{ + uint32_t linesizes[LCACHE_MAX]; + uint32_t reg[4] = {0, 0, 0, 0}; + + bzero( linesizes, sizeof(linesizes) ); + + /* AMD cpus don't support leaf-2 style cache info (it's annoying anyway). + * But apparently some kexts need this information (refer to the intel version below). + * TODO: Implement AMD cache info to intel leaf-2 format conversion. + */ + + /* + * Get deterministic cache info using AMD's cpuid registers + */ + + /* First get number of cores in the processor */ + do_cpuid(0x80000008, reg); + info_p->cpuid_cores_per_package = bitfield32(reg[ecx], 7, 0) + 1; + info_p->cpuid_logical_per_package = info_p->cpuid_cores_per_package; + + /* Get detailed cache info */ + get_amd_cache_info(info_p, linesizes, L1I, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); + get_amd_cache_info(info_p, linesizes, L1D, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); + get_amd_cache_info(info_p, linesizes, L2U, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); + get_amd_cache_info(info_p, linesizes, L3U, &vm_cache_geometry_colors, info_p->cpuid_cores_per_package); + + /* + * What linesize to publish? We use the L2 linesize if any, + * else the L1D. + */ + if ( linesizes[L2U] ) + info_p->cache_linesize = linesizes[L2U]; + else if (linesizes[L1D]) + info_p->cache_linesize = linesizes[L1D]; + else panic("no linesize"); /* AMD machines should always report a cacheline */ +} /* this function is Intel-specific */ static void cpuid_set_cache_info( i386_cpu_info_t * info_p ) @@ -382,15 +555,49 @@ * If deterministic cache parameters are not available, use * something else */ - if (info_p->cpuid_cores_per_package == 0) { + if (!cpuid_deterministic_supported) { info_p->cpuid_cores_per_package = 1; + /* mercurysquad: iterate over the predefined list of non-det cache info */ + boolean_t foundDescriptor; + intel_nd_cache_info foundInfo; + cache_type_t type = Lnone; + uint32_t colors; + uint32_t cache_sets; + + for (i = 0; i < 64; i++) { + foundDescriptor = FALSE; + for(j = 0; j < 43; j++) { + if (nonDet_CacheInfo[j].encoding == info_p->cache_info[i]) { + foundInfo = nonDet_CacheInfo[j]; + type = foundInfo.type; + foundDescriptor = TRUE; + break; + } + } + if (!foundDescriptor) continue; + + info_p->cache_size[type] = foundInfo.totalsize; + info_p->cache_sharing[type] = 1; + info_p->cache_partitions[type] = foundInfo.partitions; + linesizes[type] = foundInfo.linesize; + cache_sets = foundInfo.totalsize / + (foundInfo.associativity * + foundInfo.linesize * + foundInfo.partitions); + + colors = ( foundInfo.linesize * cache_sets ) >> 12; + + if ( colors > vm_cache_geometry_colors ) + vm_cache_geometry_colors = colors; + } + /* Apple's "something else" -- - /* cpuid define in 1024 quantities */ info_p->cache_size[L2U] = info_p->cpuid_cache_size * 1024; info_p->cache_sharing[L2U] = 1; info_p->cache_partitions[L2U] = 1; linesizes[L2U] = info_p->cpuid_cache_linesize; + */ } /* @@ -597,7 +804,7 @@ return; } - +/* static uint32_t cpuid_set_cpufamily(i386_cpu_info_t *info_p) { @@ -631,27 +838,30 @@ info_p->cpuid_cpufamily = cpufamily; return cpufamily; } - +*/ void cpuid_set_info(void) { i386_cpu_info_t *info_p = &cpuid_cpu_info; - + uint32_t dummyVar; bzero((void *)info_p, sizeof(cpuid_cpu_info)); cpuid_set_generic_info(info_p); - /* verify we are running on a supported CPU */ - if ((strncmp(CPUID_VID_INTEL, info_p->cpuid_vendor, - min(strlen(CPUID_STRING_UNKNOWN) + 1, - sizeof(info_p->cpuid_vendor)))) || - (cpuid_set_cpufamily(info_p) == CPUFAMILY_UNKNOWN)) - panic("Unsupported CPU"); +/* mercurysquad: removed the supported CPU check, and add routing for AMD cpus */ info_p->cpuid_cpu_type = CPU_TYPE_X86; info_p->cpuid_cpu_subtype = CPU_SUBTYPE_X86_ARCH1; - cpuid_set_cache_info(&cpuid_cpu_info); + if (IsAmdCPU() || PE_parse_boot_argn("-amd", &dummyVar, sizeof (dummyVar))) { + ForceAmdCpu = TRUE; // force from now so we dont have to do cpuid each time + cpuid_set_amd_cache_info(&cpuid_cpu_info); + /* The following is a non-ideal solution but seems to be required */ + if (PE_parse_boot_argn("-emulateintel", &dummyVar, sizeof (dummyVar))) + bcopy(CPUID_VID_INTEL, cpuid_cpu_info.cpuid_vendor, sizeof(CPUID_VID_INTEL)); + } else + cpuid_set_cache_info(&cpuid_cpu_info); + /* * Find the number of enabled cores and threads diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpuid.h xnu-1486.2.11/osfmk/i386/cpuid.h --- xnu-1486.2.11.orig/osfmk/i386/cpuid.h 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpuid.h 2009-11-16 22:57:59.000000000 -0500 @@ -313,6 +313,9 @@ extern void cpuid_set_info(void); +extern boolean_t IsAmdCPU(void); +extern boolean_t IsIntelCPU(void); + #ifdef __cplusplus } #endif @@ -320,4 +323,11 @@ #endif /* ASSEMBLER */ #endif /* __APPLE_API_PRIVATE */ + +/* kaitek: the following definitions are needed by tsc.c and kern_mib.c */ +#define CPU_FAMILY_PENTIUM_M (0x6) +#define CPU_FAMILY_PENTIUM_4 (0xF) +#define CPU_FAMILY_AMD_PHENOM (0x10) +#define CPU_FAMILY_AMD_SHANGHAI (0x11) +#define CPU_FAMILY_I5 (0x1E) #endif /* _MACHINE_CPUID_H_ */ diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpuid.h.orig xnu-1486.2.11/osfmk/i386/cpuid.h.orig --- xnu-1486.2.11.orig/osfmk/i386/cpuid.h.orig 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpuid.h.orig 2009-11-16 22:29:44.000000000 -0500 @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2000-2006 Apple Computer, 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@ + */ +/* + * @OSF_COPYRIGHT@ + */ + +/* + * x86 CPU identification + * + */ + +#ifndef _MACHINE_CPUID_H_ +#define _MACHINE_CPUID_H_ + +#include + +#ifdef __APPLE_API_PRIVATE + +#define CPUID_VID_INTEL "GenuineIntel" +#define CPUID_VID_AMD "AuthenticAMD" + +#define CPUID_STRING_UNKNOWN "Unknown CPU Typ" + +#define _Bit(n) (1ULL << n) +#define _HBit(n) (1ULL << ((n)+32)) + +/* + * The CPUID_FEATURE_XXX values define 64-bit values + * returned in %ecx:%edx to a CPUID request with %eax of 1: + */ +#define CPUID_FEATURE_FPU _Bit(0) /* Floating point unit on-chip */ +#define CPUID_FEATURE_VME _Bit(1) /* Virtual Mode Extension */ +#define CPUID_FEATURE_DE _Bit(2) /* Debugging Extension */ +#define CPUID_FEATURE_PSE _Bit(3) /* Page Size Extension */ +#define CPUID_FEATURE_TSC _Bit(4) /* Time Stamp Counter */ +#define CPUID_FEATURE_MSR _Bit(5) /* Model Specific Registers */ +#define CPUID_FEATURE_PAE _Bit(6) /* Physical Address Extension */ +#define CPUID_FEATURE_MCE _Bit(7) /* Machine Check Exception */ +#define CPUID_FEATURE_CX8 _Bit(8) /* CMPXCHG8B */ +#define CPUID_FEATURE_APIC _Bit(9) /* On-chip APIC */ +#define CPUID_FEATURE_SEP _Bit(11) /* Fast System Call */ +#define CPUID_FEATURE_MTRR _Bit(12) /* Memory Type Range Register */ +#define CPUID_FEATURE_PGE _Bit(13) /* Page Global Enable */ +#define CPUID_FEATURE_MCA _Bit(14) /* Machine Check Architecture */ +#define CPUID_FEATURE_CMOV _Bit(15) /* Conditional Move Instruction */ +#define CPUID_FEATURE_PAT _Bit(16) /* Page Attribute Table */ +#define CPUID_FEATURE_PSE36 _Bit(17) /* 36-bit Page Size Extension */ +#define CPUID_FEATURE_PSN _Bit(18) /* Processor Serial Number */ +#define CPUID_FEATURE_CLFSH _Bit(19) /* CLFLUSH Instruction supported */ +#define CPUID_FEATURE_DS _Bit(21) /* Debug Store */ +#define CPUID_FEATURE_ACPI _Bit(22) /* Thermal monitor and Clock Ctrl */ +#define CPUID_FEATURE_MMX _Bit(23) /* MMX supported */ +#define CPUID_FEATURE_FXSR _Bit(24) /* Fast floating pt save/restore */ +#define CPUID_FEATURE_SSE _Bit(25) /* Streaming SIMD extensions */ +#define CPUID_FEATURE_SSE2 _Bit(26) /* Streaming SIMD extensions 2 */ +#define CPUID_FEATURE_SS _Bit(27) /* Self-Snoop */ +#define CPUID_FEATURE_HTT _Bit(28) /* Hyper-Threading Technology */ +#define CPUID_FEATURE_TM _Bit(29) /* Thermal Monitor (TM1) */ +#define CPUID_FEATURE_PBE _Bit(31) /* Pend Break Enable */ + +#define CPUID_FEATURE_SSE3 _HBit(0) /* Streaming SIMD extensions 3 */ +#define CPUID_FEATURE_MONITOR _HBit(3) /* Monitor/mwait */ +#define CPUID_FEATURE_DSCPL _HBit(4) /* Debug Store CPL */ +#define CPUID_FEATURE_VMX _HBit(5) /* VMX */ +#define CPUID_FEATURE_SMX _HBit(6) /* SMX */ +#define CPUID_FEATURE_EST _HBit(7) /* Enhanced SpeedsTep (GV3) */ +#define CPUID_FEATURE_TM2 _HBit(8) /* Thermal Monitor 2 */ +#define CPUID_FEATURE_SSSE3 _HBit(9) /* Supplemental SSE3 instructions */ +#define CPUID_FEATURE_CID _HBit(10) /* L1 Context ID */ +#define CPUID_FEATURE_CX16 _HBit(13) /* CmpXchg16b instruction */ +#define CPUID_FEATURE_xTPR _HBit(14) /* Send Task PRiority msgs */ +#define CPUID_FEATURE_PDCM _HBit(15) /* Perf/Debug Capability MSR */ +#define CPUID_FEATURE_DCA _HBit(18) /* Direct Cache Access */ +#define CPUID_FEATURE_SSE4_1 _HBit(19) /* Streaming SIMD extensions 4.1 */ +#define CPUID_FEATURE_SSE4_2 _HBit(20) /* Streaming SIMD extensions 4.2 */ +#define CPUID_FEATURE_xAPIC _HBit(21) /* Extended APIC Mode */ +#define CPUID_FEATURE_POPCNT _HBit(23) /* POPCNT instruction */ +#define CPUID_FEATURE_VMM _HBit(31) /* VMM (Hypervisor) present */ + +/* + * The CPUID_EXTFEATURE_XXX values define 64-bit values + * returned in %ecx:%edx to a CPUID request with %eax of 0x80000001: + */ +#define CPUID_EXTFEATURE_SYSCALL _Bit(11) /* SYSCALL/sysret */ +#define CPUID_EXTFEATURE_XD _Bit(20) /* eXecute Disable */ +#define CPUID_EXTFEATURE_RDTSCP _Bit(27) /* RDTSCP */ +#define CPUID_EXTFEATURE_EM64T _Bit(29) /* Extended Mem 64 Technology */ + +#define CPUID_EXTFEATURE_LAHF _HBit(20) /* LAFH/SAHF instructions */ + +/* + * The CPUID_EXTFEATURE_XXX values define 64-bit values + * returned in %ecx:%edx to a CPUID request with %eax of 0x80000007: + */ +#define CPUID_EXTFEATURE_TSCI _Bit(8) /* TSC Invariant */ + +#define CPUID_CACHE_SIZE 16 /* Number of descriptor values */ + +#define CPUID_MWAIT_EXTENSION _Bit(0) /* enumeration of WMAIT extensions */ +#define CPUID_MWAIT_BREAK _Bit(1) /* interrupts are break events */ + +#define CPUID_MODEL_YONAH 14 +#define CPUID_MODEL_MEROM 15 +#define CPUID_MODEL_PENRYN 23 +#define CPUID_MODEL_NEHALEM 26 +#define CPUID_MODEL_ATOM 28 +#define CPUID_MODEL_FIELDS 30 /* Lynnfield, Clarksfield, Jasper */ +#define CPUID_MODEL_DALES 31 /* Havendale, Auburndale */ +#define CPUID_MODEL_NEHALEM_EX 46 + +#ifndef ASSEMBLER +#include +#include +#include +#include + + +typedef enum { eax, ebx, ecx, edx } cpuid_register_t; +static inline void +cpuid(uint32_t *data) +{ + asm("cpuid" + : "=a" (data[eax]), + "=b" (data[ebx]), + "=c" (data[ecx]), + "=d" (data[edx]) + : "a" (data[eax]), + "b" (data[ebx]), + "c" (data[ecx]), + "d" (data[edx])); +} +static inline void +do_cpuid(uint32_t selector, uint32_t *data) +{ + asm("cpuid" + : "=a" (data[0]), + "=b" (data[1]), + "=c" (data[2]), + "=d" (data[3]) + : "a"(selector)); +} + +/* + * Cache ID descriptor structure, used to parse CPUID leaf 2. + * Note: not used in kernel. + */ +typedef enum { Lnone, L1I, L1D, L2U, L3U, LCACHE_MAX } cache_type_t ; +typedef struct { + unsigned char value; /* Descriptor value */ + cache_type_t type; /* Cache type */ + unsigned int size; /* Cache size */ + unsigned int linesize; /* Cache line size */ +#ifdef KERNEL + const char *description; /* Cache description */ +#endif /* KERNEL */ +} cpuid_cache_desc_t; + +#ifdef KERNEL +#define CACHE_DESC(value,type,size,linesize,text) \ + { value, type, size, linesize, text } +#else +#define CACHE_DESC(value,type,size,linesize,text) \ + { value, type, size, linesize } +#endif /* KERNEL */ + +/* Monitor/mwait Leaf: */ +typedef struct { + uint32_t linesize_min; + uint32_t linesize_max; + uint32_t extensions; + uint32_t sub_Cstates; +} cpuid_mwait_leaf_t; + +/* Thermal and Power Management Leaf: */ +typedef struct { + boolean_t sensor; + boolean_t dynamic_acceleration; + uint32_t thresholds; + boolean_t ACNT_MCNT; +} cpuid_thermal_leaf_t; + +/* Architectural Performance Monitoring Leaf: */ +typedef struct { + uint8_t version; + uint8_t number; + uint8_t width; + uint8_t events_number; + uint32_t events; + uint8_t fixed_number; + uint8_t fixed_width; +} cpuid_arch_perf_leaf_t; + +/* Physical CPU info - this is exported out of the kernel (kexts), so be wary of changes */ +typedef struct { + char cpuid_vendor[16]; + char cpuid_brand_string[48]; + const char *cpuid_model_string; + + cpu_type_t cpuid_type; /* this is *not* a cpu_type_t in our */ + uint8_t cpuid_family; + uint8_t cpuid_model; + uint8_t cpuid_extmodel; + uint8_t cpuid_extfamily; + uint8_t cpuid_stepping; + uint64_t cpuid_features; + uint64_t cpuid_extfeatures; + uint32_t cpuid_signature; + uint8_t cpuid_brand; + + uint32_t cache_size[LCACHE_MAX]; + uint32_t cache_linesize; + + uint8_t cache_info[64]; /* list of cache descriptors */ + + uint32_t cpuid_cores_per_package; + uint32_t cpuid_logical_per_package; + uint32_t cache_sharing[LCACHE_MAX]; + uint32_t cache_partitions[LCACHE_MAX]; + + cpu_type_t cpuid_cpu_type; /* */ + cpu_subtype_t cpuid_cpu_subtype; /* */ + + /* Per-vendor info */ + cpuid_mwait_leaf_t cpuid_mwait_leaf; +#define cpuid_mwait_linesize_max cpuid_mwait_leaf.linesize_max +#define cpuid_mwait_linesize_min cpuid_mwait_leaf.linesize_min +#define cpuid_mwait_extensions cpuid_mwait_leaf.extensions +#define cpuid_mwait_sub_Cstates cpuid_mwait_leaf.sub_Cstates + cpuid_thermal_leaf_t cpuid_thermal_leaf; + cpuid_arch_perf_leaf_t cpuid_arch_perf_leaf; + + /* Cache details: */ + uint32_t cpuid_cache_linesize; + uint32_t cpuid_cache_L2_associativity; + uint32_t cpuid_cache_size; + + /* Virtual and physical address aize: */ + uint32_t cpuid_address_bits_physical; + uint32_t cpuid_address_bits_virtual; + + uint32_t cpuid_microcode_version; + + /* Numbers of tlbs per processor [i|d, small|large, level0|level1] */ + uint32_t cpuid_tlb[2][2][2]; + #define TLB_INST 0 + #define TLB_DATA 1 + #define TLB_SMALL 0 + #define TLB_LARGE 1 + uint32_t cpuid_stlb; + + uint32_t core_count; + uint32_t thread_count; + + /* Max leaf ids available from CPUID */ + uint32_t cpuid_max_basic; + uint32_t cpuid_max_ext; + + /* Family-specific info links */ + uint32_t cpuid_cpufamily; + cpuid_mwait_leaf_t *cpuid_mwait_leafp; + cpuid_thermal_leaf_t *cpuid_thermal_leafp; + cpuid_arch_perf_leaf_t *cpuid_arch_perf_leafp; + +} i386_cpu_info_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * External declarations + */ +extern cpu_type_t cpuid_cputype(void); +extern cpu_subtype_t cpuid_cpusubtype(void); +extern void cpuid_cpu_display(const char *); +extern void cpuid_feature_display(const char *); +extern void cpuid_extfeature_display(const char *); +extern char * cpuid_get_feature_names(uint64_t, char *, unsigned); +extern char * cpuid_get_extfeature_names(uint64_t, char *, unsigned); + +extern uint64_t cpuid_features(void); +extern uint64_t cpuid_extfeatures(void); +extern uint32_t cpuid_family(void); +extern uint32_t cpuid_cpufamily(void); + +extern void cpuid_get_info(i386_cpu_info_t *info_p); +extern i386_cpu_info_t *cpuid_info(void); + +extern void cpuid_set_info(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ASSEMBLER */ + +#endif /* __APPLE_API_PRIVATE */ +#endif /* _MACHINE_CPUID_H_ */ diff -Naur xnu-1486.2.11.orig/osfmk/i386/cpuid_legacy.h xnu-1486.2.11/osfmk/i386/cpuid_legacy.h --- xnu-1486.2.11.orig/osfmk/i386/cpuid_legacy.h 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/cpuid_legacy.h 2009-11-16 22:57:59.000000000 -0500 @@ -0,0 +1,84 @@ +/* + * cpuid_legacy.h + * xnu + * + * Created by mercurysquad on 21/9/08. + * + */ + +/* This file is added to avoid polluting stock cpuid.h */ + +#ifndef _CPUID_LEGACY_H_ +#define _CPUID_LEGACY_H_ + +/* Declarations for extra stuff added to cpuid.c */ +void get_amd_cache_info( + i386_cpu_info_t* info_p, + uint32_t linesizes[], + cache_type_t type, + uint32_t* geometry_colors, + uint32_t ncores); + +/* Declarations for non deterministic cache info */ +typedef struct { + uint32_t encoding; + cache_type_t type; + uint32_t totalsize; + uint32_t associativity; + uint32_t linesize; + uint32_t partitions; +} intel_nd_cache_info; + +//#define KB (1024) +#define MB (1024*KB) + +/* For encoding information, refer to IA32 instruction set reference A-M, CPUID instruction */ +/* Wonder who at Intel came up with this mess */ +const intel_nd_cache_info nonDet_CacheInfo[43] = { +/* byte, type, size, assoc, linesize */ +{ 0x06, L1I, 8*KB, 4, 32, 1 }, +{ 0x08, L1I, 16*KB, 4, 32, 1 }, +{ 0x0A, L1D, 8*KB, 2, 32, 1 }, +{ 0x0C, L1D, 16*KB, 4, 32, 1 }, +{ 0x0E, L1D, 24*KB, 6, 64, 1 }, +{ 0x22, L3U, 512*KB, 4, 64, 2 }, +{ 0x23, L3U, 1*MB, 8, 64, 2 }, +{ 0x25, L3U, 2*MB, 8, 64, 2 }, +{ 0x29, L3U, 4*MB, 8, 64, 2 }, +{ 0x2C, L1D, 32*KB, 8, 64, 2 }, +{ 0x30, L1I, 32*KB, 8, 64, 1 }, +{ 0x41, L2U, 128*KB, 4, 32, 1 }, +{ 0x42, L2U, 256*KB, 4, 32, 1 }, +{ 0x43, L2U, 512*KB, 4, 32, 1 }, +{ 0x44, L2U, 1*MB, 4, 32, 1 }, +{ 0x45, L2U, 2*MB, 4, 32, 1 }, +{ 0x46, L3U, 4*MB, 4, 64, 1 }, +{ 0x47, L3U, 8*MB, 8, 64, 1 }, +{ 0x48, L2U, 3*MB, 12, 64, 1 }, +{ 0x49, L2U, 4*MB, 16, 64, 1 }, // for Xeons family Fh model 6h it's L3U but we dont care +{ 0x4A, L3U, 6*MB, 12, 64, 1 }, +{ 0x4B, L3U, 8*MB, 16, 64, 1 }, +{ 0x4C, L3U, 12*MB, 12, 64, 1 }, +{ 0x4D, L3U, 16*MB, 16, 64, 1 }, +{ 0x4E, L2U, 6*MB, 24, 64, 1 }, +{ 0x60, L1D, 16*KB, 8, 64, 1 }, +{ 0x66, L1D, 8*KB, 4, 64, 1 }, +{ 0x67, L1D, 16*KB, 4, 64, 1 }, +{ 0x68, L1D, 32*KB, 4, 64, 1 }, +{ 0x78, L2U, 1*MB, 4, 64, 1 }, +{ 0x79, L2U, 128*KB, 8, 64, 2 }, +{ 0x7A, L2U, 256*KB, 8, 64, 2 }, +{ 0x7B, L2U, 512*KB, 8, 64, 2 }, +{ 0x7C, L2U, 1*MB, 8, 64, 2 }, +{ 0x7D, L2U, 2*MB, 8, 64, 1 }, +{ 0x7F, L2U, 512*KB, 2, 64, 1 }, +{ 0x80, L2U, 512*KB, 8, 64, 1 }, +{ 0x82, L2U, 256*KB, 8, 32, 1 }, +{ 0x83, L2U, 512*KB, 8, 32, 1 }, +{ 0x84, L2U, 1*MB, 8, 32, 1 }, +{ 0x85, L2U, 2*MB, 8, 32, 1 }, +{ 0x86, L2U, 512*KB, 4, 64, 1 }, +{ 0x87, L2U, 1*MB, 8, 64, 1 } +}; + +#endif // _CPUID_LEGACY_H_ diff -Naur xnu-1486.2.11.orig/osfmk/i386/i386_init.c xnu-1486.2.11/osfmk/i386/i386_init.c --- xnu-1486.2.11.orig/osfmk/i386/i386_init.c 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/i386_init.c 2009-11-16 22:57:59.000000000 -0500 @@ -476,9 +476,14 @@ * At this point we check whether we are a 64-bit processor * and that we're not restricted to legacy mode, 32-bit operation. */ + uint32_t boot_arg; if (cpuid_extfeatures() & CPUID_EXTFEATURE_EM64T) { kprintf("EM64T supported"); - if (PE_parse_boot_argn("-legacy", &legacy_mode, sizeof (legacy_mode))) { + if (PE_parse_boot_argn("-legacy", &legacy_mode, sizeof (legacy_mode)) || + /* mercurysquad: SSE2 processor can't run in 64bit */ + !(cpuid_features() & CPUID_FEATURE_SSE3) || + /* Also force 32bit until bcopy is fixed */ + IsAmdCPU() || IsIntelCPU()) { kprintf(" but legacy mode forced\n"); IA32e = FALSE; } else { @@ -486,6 +491,10 @@ } } else IA32e = FALSE; + if (PE_parse_boot_argn("-force64", &boot_arg, sizeof(boot_arg))) { + kprintf("EM64T forced\n"); + IA32e = TRUE; + } #endif if (!(cpuid_extfeatures() & CPUID_EXTFEATURE_XD)) diff -Naur xnu-1486.2.11.orig/osfmk/i386/idt.s xnu-1486.2.11/osfmk/i386/idt.s --- xnu-1486.2.11.orig/osfmk/i386/idt.s 2009-11-12 13:00:02.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/idt.s 2009-11-16 22:57:59.000000000 -0500 @@ -456,8 +456,13 @@ INTERRUPT(0xf8) INTERRUPT(0xf9) INTERRUPT(0xfa) -INTERRUPT(0xfb) -INTERRUPT(0xfc) +/* semthex / mercurysquad: + * This next interrupt (0xFB) is used for fake CPUID on AMD or other CPUs. + * The binary patcher replaces all cpuid instructions in a Mach-O binary with int 0xfb, + * which is then caught by this handler which sends Intel cpuid instead of AMD or whatever. + */ +EXCEP_SPC_USR(0xfb, t_fake_cpuid) +EXCEP_SPC_USR(0xfc, t_fake_sysenter) INTERRUPT(0xfd) INTERRUPT(0xfe) EXCEPTION(0xff,t_preempt) @@ -470,6 +475,31 @@ .text +/* semthex / mercurysquad: +* The following is the int 0xfb handler which will return a fake cpuid string +*/ +Entry(t_fake_cpuid) + pushf // Must not modify eflags! + testl %eax, %eax // If eax == 0, we are getting cpuid string, so compare and set ZF for later + cpuid // Get cpuid; we'll patch it later based on the above test + // Note that cpuid does not affect any flags, so it's ok to compare + // first, do cpuid, and then do the conditional jump + jnz 2f // eax was not zero: we are getting feature bits etc, so skip patching +1: // otherwise, eax was zero so patch the cpuid vendor string with GenuineIntel + movl $0x756e6547, %ebx // "Genu" + movl $0x49656e69, %edx // "ineI" + movl $0x6c65746e, %ecx // "ntel" +2: + popf // restore eflags + iret // Done. Return from interrupt + +Entry(t_fake_sysenter) + pushl %eax /* save system call number */ + pushl $0 /* clear trap number slot */ + pusha /* save the general registers */ + movl $EXT(lo_sysenter),%ebx + jmp enter_lohandler + /******************************************************************************************************* * diff -Naur xnu-1486.2.11.orig/osfmk/i386/idt64.s xnu-1486.2.11/osfmk/i386/idt64.s --- xnu-1486.2.11.orig/osfmk/i386/idt64.s 2009-11-12 13:00:02.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/idt64.s 2009-11-16 22:57:59.000000000 -0500 @@ -425,14 +425,39 @@ INTERRUPT64(0xf8) INTERRUPT64(0xf9) INTERRUPT64(0xfa) -INTERRUPT64(0xfb) -INTERRUPT64(0xfc) +EXCEP64_SPC_USR(0xfb, t64_fake_cpuid) +EXCEP64_SPC_USR(0xfc, t64_fake_sysenter) INTERRUPT64(0xfd) INTERRUPT64(0xfe) EXCEPTION64(0xff,t64_preempt) .text + + +/* kaitek: for an in-depth description of the int 0xfb handler, see t_fake_cpuid in idt.s */ + +Entry(t64_fake_cpuid) + pushf + testl %eax, %eax // cpuid only checks the lower dword of rax + cpuid + jnz 2f +1: + movq $0x756e6547, %rbx // results, however, are zero-extended to qword size + movq $0x49656e69, %rdx + movq $0x6c65746e, %rcx +2: + popf + iretq // operand size is *very* important here + +Entry(t64_fake_sysenter) + swapgs /* switch to kernel gs (cpu_data) */ + push %rax /* save system call number */ + push $(UNIX_INT) /* only used for statistics */ + movl $EXT(lo_sysenter),4(%rsp) + jmp L_32bit_enter_check + + /* * * Trap/interrupt entry points. diff -Naur xnu-1486.2.11.orig/osfmk/i386/lapic.c xnu-1486.2.11/osfmk/i386/lapic.c --- xnu-1486.2.11.orig/osfmk/i386/lapic.c 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/lapic.c 2009-11-16 22:58:00.000000000 -0500 @@ -211,7 +211,8 @@ lapic_id = (unsigned long)(lapic_start + LAPIC_ID); if ((LAPIC_READ(VERSION)&LAPIC_VERSION_MASK) < 0x14) { - panic("Local APIC version 0x%x, 0x14 or more expected\n", +// qoopz: panics on some amds + printf("Local APIC version 0x%x, 0x14 or more expected\n", (LAPIC_READ(VERSION)&LAPIC_VERSION_MASK)); } diff -Naur xnu-1486.2.11.orig/osfmk/i386/pmCPU.c xnu-1486.2.11/osfmk/i386/pmCPU.c --- xnu-1486.2.11.orig/osfmk/i386/pmCPU.c 2009-11-12 12:59:56.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/pmCPU.c 2009-11-16 22:58:00.000000000 -0500 @@ -658,6 +658,7 @@ callbacks->GetNanotimeInfo = pmGetNanotimeInfo; callbacks->topoParms = &topoParms; } else { +// qoopz: diff kernel crashes between versions, need cpupm disabler. panic("Version mis-match between Kernel and CPU PM"); } diff -Naur xnu-1486.2.11.orig/osfmk/i386/pmCPU.c.orig xnu-1486.2.11/osfmk/i386/pmCPU.c.orig --- xnu-1486.2.11.orig/osfmk/i386/pmCPU.c.orig 1969-12-31 19:00:00.000000000 -0500 +++ xnu-1486.2.11/osfmk/i386/pmCPU.c.orig 2009-11-16 22:29:44.000000000 -0500 @@ -0,0 +1,722 @@ +/* + * Copyright (c) 2004-2009 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@ + */ + +/* + * CPU-specific power management support. + * + * Implements the "wrappers" to the KEXT. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Kernel parameter determining whether threads are halted unconditionally + * in the idle state. This is the default behavior. + * See machine_idle() for use. + */ +int idlehalt = 1; + +extern int disableConsoleOutput; + +decl_simple_lock_data(,pm_init_lock); + +/* + * The following is set when the KEXT loads and initializes. + */ +pmDispatch_t *pmDispatch = NULL; + +static uint32_t pmInitDone = 0; + + +/* + * Initialize the Cstate change code. + */ +void +power_management_init(void) +{ + static boolean_t initialized = FALSE; + + /* + * Initialize the lock for the KEXT initialization. + */ + if (!initialized) { + simple_lock_init(&pm_init_lock, 0); + initialized = TRUE; + } + + if (pmDispatch != NULL && pmDispatch->cstateInit != NULL) + (*pmDispatch->cstateInit)(); +} + +/* + * Called when the CPU is idle. It calls into the power management kext + * to determine the best way to idle the CPU. + */ +void +machine_idle(void) +{ + cpu_data_t *my_cpu = current_cpu_datap(); + + if (my_cpu == NULL) + goto out; + + /* + * If idlehalt isn't set, then don't do any power management related + * idle handling. + */ + if (!idlehalt) + goto out; + + my_cpu->lcpu.state = LCPU_IDLE; + DBGLOG(cpu_handle, cpu_number(), MP_IDLE); + MARK_CPU_IDLE(cpu_number()); + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->cstateMachineIdle != NULL) + (*pmDispatch->cstateMachineIdle)(0x7FFFFFFFFFFFFFFFULL); + else { + /* + * If no power management, re-enable interrupts and halt. + * This will keep the CPU from spinning through the scheduler + * and will allow at least some minimal power savings (but it + * cause problems in some MP configurations w.r.t. the APIC + * stopping during a GV3 transition). + */ + __asm__ volatile ("sti; hlt"); + } + + /* + * Mark the CPU as running again. + */ + MARK_CPU_ACTIVE(cpu_number()); + DBGLOG(cpu_handle, cpu_number(), MP_UNIDLE); + my_cpu->lcpu.state = LCPU_RUN; + + /* + * Re-enable interrupts. + */ + out: + __asm__ volatile("sti"); +} + +/* + * Called when the CPU is to be halted. It will choose the best C-State + * to be in. + */ +void +pmCPUHalt(uint32_t reason) +{ + cpu_data_t *cpup = current_cpu_datap(); + + switch (reason) { + case PM_HALT_DEBUG: + cpup->lcpu.state = LCPU_PAUSE; + __asm__ volatile ("wbinvd; hlt"); + break; + + case PM_HALT_PANIC: + cpup->lcpu.state = LCPU_PAUSE; + __asm__ volatile ("cli; wbinvd; hlt"); + break; + + case PM_HALT_NORMAL: + default: + __asm__ volatile ("cli"); + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->pmCPUHalt != NULL) { + /* + * Halt the CPU (and put it in a low power state. + */ + (*pmDispatch->pmCPUHalt)(); + + /* + * We've exited halt, so get the the CPU schedulable again. + */ + i386_init_slave_fast(); + + panic("init_slave_fast returned"); + } else { + /* + * If no power managment and a processor is taken off-line, + * then invalidate the cache and halt it (it will not be able + * to be brought back on-line without resetting the CPU). + */ + __asm__ volatile ("wbinvd"); + cpup->lcpu.state = LCPU_HALT; + __asm__ volatile ( "wbinvd; hlt" ); + + panic("back from Halt"); + } + break; + } +} + +void +pmMarkAllCPUsOff(void) +{ + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->markAllCPUsOff != NULL) + (*pmDispatch->markAllCPUsOff)(); +} + +static void +pmInitComplete(void) +{ + pmInitDone = 1; +} + +static x86_lcpu_t * +pmGetLogicalCPU(int cpu) +{ + return(cpu_to_lcpu(cpu)); +} + +static x86_lcpu_t * +pmGetMyLogicalCPU(void) +{ + cpu_data_t *cpup = current_cpu_datap(); + + return(&cpup->lcpu); +} + +static x86_core_t * +pmGetCore(int cpu) +{ + return(cpu_to_core(cpu)); +} + +static x86_core_t * +pmGetMyCore(void) +{ + cpu_data_t *cpup = current_cpu_datap(); + + return(cpup->lcpu.core); +} + +static x86_die_t * +pmGetDie(int cpu) +{ + return(cpu_to_die(cpu)); +} + +static x86_die_t * +pmGetMyDie(void) +{ + cpu_data_t *cpup = current_cpu_datap(); + + return(cpup->lcpu.die); +} + +static x86_pkg_t * +pmGetPackage(int cpu) +{ + return(cpu_to_package(cpu)); +} + +static x86_pkg_t * +pmGetMyPackage(void) +{ + cpu_data_t *cpup = current_cpu_datap(); + + return(cpup->lcpu.package); +} + +static void +pmLockCPUTopology(int lock) +{ + if (lock) { + simple_lock(&x86_topo_lock); + } else { + simple_unlock(&x86_topo_lock); + } +} + +/* + * Called to get the next deadline that has been set by the + * power management code. + */ +uint64_t +pmCPUGetDeadline(cpu_data_t *cpu) +{ + uint64_t deadline = EndOfAllTime; + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->GetDeadline != NULL) + deadline = (*pmDispatch->GetDeadline)(&cpu->lcpu); + + return(deadline); +} + +/* + * Called to determine if the supplied deadline or the power management + * deadline is sooner. Returns which ever one is first. + */ +uint64_t +pmCPUSetDeadline(cpu_data_t *cpu, uint64_t deadline) +{ + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->SetDeadline != NULL) + deadline = (*pmDispatch->SetDeadline)(&cpu->lcpu, deadline); + + return(deadline); +} + +/* + * Called when a power management deadline expires. + */ +void +pmCPUDeadline(cpu_data_t *cpu) +{ + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->Deadline != NULL) + (*pmDispatch->Deadline)(&cpu->lcpu); +} + +/* + * Called to get a CPU out of idle. + */ +boolean_t +pmCPUExitIdle(cpu_data_t *cpu) +{ + boolean_t do_ipi; + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->exitIdle != NULL) + do_ipi = (*pmDispatch->exitIdle)(&cpu->lcpu); + else + do_ipi = TRUE; + + return(do_ipi); +} + +kern_return_t +pmCPUExitHalt(int cpu) +{ + kern_return_t rc = KERN_INVALID_ARGUMENT; + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->exitHalt != NULL) + rc = pmDispatch->exitHalt(cpu_to_lcpu(cpu)); + + return(rc); +} + +kern_return_t +pmCPUExitHaltToOff(int cpu) +{ + kern_return_t rc = KERN_INVALID_ARGUMENT; + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->exitHaltToOff != NULL) + rc = pmDispatch->exitHaltToOff(cpu_to_lcpu(cpu)); + + return(rc); +} + +/* + * Called to initialize the power management structures for the CPUs. + */ +void +pmCPUStateInit(void) +{ + if (pmDispatch != NULL && pmDispatch->pmCPUStateInit != NULL) + (*pmDispatch->pmCPUStateInit)(); +} + +/* + * Called when a CPU is being restarted after being powered off (as in S3). + */ +void +pmCPUMarkRunning(cpu_data_t *cpu) +{ + cpu_data_t *cpup = current_cpu_datap(); + + if (pmInitDone + && pmDispatch != NULL + && pmDispatch->markCPURunning != NULL) + (*pmDispatch->markCPURunning)(&cpu->lcpu); + else + cpup->lcpu.state = LCPU_RUN; +} + +/* + * Called to get/set CPU power management state. + */ +int +pmCPUControl(uint32_t cmd, void *datap) +{ + int rc = -1; + + if (pmDispatch != NULL + && pmDispatch->pmCPUControl != NULL) + rc = (*pmDispatch->pmCPUControl)(cmd, datap); + + return(rc); +} + +/* + * Called to save the timer state used by power management prior + * to "sleeping". + */ +void +pmTimerSave(void) +{ + if (pmDispatch != NULL + && pmDispatch->pmTimerStateSave != NULL) + (*pmDispatch->pmTimerStateSave)(); +} + +/* + * Called to restore the timer state used by power management after + * waking from "sleep". + */ +void +pmTimerRestore(void) +{ + if (pmDispatch != NULL + && pmDispatch->pmTimerStateRestore != NULL) + (*pmDispatch->pmTimerStateRestore)(); +} + +/* + * Set the worst-case time for the C4 to C2 transition. + * No longer does anything. + */ +void +ml_set_maxsnoop(__unused uint32_t maxdelay) +{ +} + + +/* + * Get the worst-case time for the C4 to C2 transition. Returns nanoseconds. + */ +unsigned +ml_get_maxsnoop(void) +{ + uint64_t max_snoop = 0; + + if (pmDispatch != NULL + && pmDispatch->getMaxSnoop != NULL) + max_snoop = pmDispatch->getMaxSnoop(); + + return((unsigned)(max_snoop & 0xffffffff)); +} + + +uint32_t +ml_get_maxbusdelay(void) +{ + uint64_t max_delay = 0; + + if (pmDispatch != NULL + && pmDispatch->getMaxBusDelay != NULL) + max_delay = pmDispatch->getMaxBusDelay(); + + return((uint32_t)(max_delay & 0xffffffff)); +} + +/* + * Set the maximum delay time allowed for snoop on the bus. + * + * Note that this value will be compared to the amount of time that it takes + * to transition from a non-snooping power state (C4) to a snooping state (C2). + * If maxBusDelay is less than C4C2SnoopDelay, + * we will not enter the lowest power state. + */ +void +ml_set_maxbusdelay(uint32_t mdelay) +{ + uint64_t maxdelay = mdelay; + + if (pmDispatch != NULL + && pmDispatch->setMaxBusDelay != NULL) + pmDispatch->setMaxBusDelay(maxdelay); +} + +uint64_t +ml_get_maxintdelay(void) +{ + uint64_t max_delay = 0; + + if (pmDispatch != NULL + && pmDispatch->getMaxIntDelay != NULL) + max_delay = pmDispatch->getMaxIntDelay(); + + return(max_delay); +} + +/* + * Set the maximum delay allowed for an interrupt. + */ +void +ml_set_maxintdelay(uint64_t mdelay) +{ + if (pmDispatch != NULL + && pmDispatch->setMaxIntDelay != NULL) + pmDispatch->setMaxIntDelay(mdelay); +} + +/* + * Put a CPU into "safe" mode with respect to power. + * + * Some systems cannot operate at a continuous "normal" speed without + * exceeding the thermal design. This is called per-CPU to place the + * CPUs into a "safe" operating mode. + */ +void +pmSafeMode(x86_lcpu_t *lcpu, uint32_t flags) +{ + if (pmDispatch != NULL + && pmDispatch->pmCPUSafeMode != NULL) + pmDispatch->pmCPUSafeMode(lcpu, flags); + else { + /* + * Do something reasonable if the KEXT isn't present. + * + * We only look at the PAUSE and RESUME flags. The other flag(s) + * will not make any sense without the KEXT, so just ignore them. + * + * We set the CPU's state to indicate that it's halted. If this + * is the CPU we're currently running on, then spin until the + * state becomes non-halted. + */ + if (flags & PM_SAFE_FL_PAUSE) { + lcpu->state = LCPU_PAUSE; + if (lcpu == x86_lcpu()) { + while (lcpu->state == LCPU_PAUSE) + cpu_pause(); + } + } + + /* + * Clear the halted flag for the specified CPU, that will + * get it out of it's spin loop. + */ + if (flags & PM_SAFE_FL_RES