/* * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights * Reserved. 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. Please obtain a copy of the * License at http://www.apple.com/publicsource 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * drivers.c - Driver Loading Functions. * * Copyright (c) 2000 Apple Computer, Inc. * * DRI: Josh de Cesare */ /* Copyright 2007 VMware Inc. "Preboot" ramdisk support added by David Elliott */ #include #include #include #include "sl.h" #include "boot.h" #include "bootstruct.h" #include "xml.h" struct Module { struct Module *nextModule; long willLoad; TagPtr dict; char *plistAddr; long plistLength; char *driverPath; }; typedef struct Module Module, *ModulePtr; struct DriverInfo { char *plistAddr; long plistLength; void *moduleAddr; long moduleLength; // The next 2 fields are required starting with Mac OS 10.6 build 10a261. char *pathAddr; long pathLength; }; typedef struct DriverInfo DriverInfo, *DriverInfoPtr; #define kDriverPackageSignature1 'MKXT' #define kDriverPackageSignature2 'MOSX' struct DriversPackage { unsigned long signature1; unsigned long signature2; unsigned long length; unsigned long alder32; unsigned long version; unsigned long numDrivers; unsigned long reserved1; unsigned long reserved2; }; typedef struct DriversPackage DriversPackage; enum { kCFBundleType2, kCFBundleType3 }; long (*LoadExtraDrivers_p)(FileLoadDrivers_t FileLoadDrivers_p); static unsigned long Alder32( unsigned char * buffer, long length ); static long SnowLeopardFileLoadDrivers( char * dirSpec ); static long FileLoadDrivers(char *dirSpec, long plugin); static long NetLoadDrivers(char *dirSpec); static long LoadDriverMKext(char *fileSpec); static long LoadDriverPList(char *dirSpec, char *name, long bundleType); static long LoadMatchedModules(void); static long MatchPersonalities(void); static long MatchLibraries(void); #ifdef NOTDEF static ModulePtr FindModule(char *name); static void ThinFatFile(void **loadAddrP, unsigned long *lengthP); #endif static long ParseXML(char *buffer, ModulePtr *module, TagPtr *personalities); static long InitDriverSupport(void); static ModulePtr gModuleHead, gModuleTail; static TagPtr gPersonalityHead, gPersonalityTail; static char * gExtensionsSpec; static char * gDriverSpec; static char * gFileSpec; static char * gTempSpec; static char * gFileName; // Filled in by selectPrebootVolume if a suitable volume is found. char *gPrebootExtraDir = NULL; static unsigned long Alder32( unsigned char * buffer, long length ) { long cnt; unsigned long result, lowHalf, highHalf; lowHalf = 1; highHalf = 0; for ( cnt = 0; cnt < length; cnt++ ) { if ((cnt % 5000) == 0) { lowHalf %= 65521L; highHalf %= 65521L; } lowHalf += buffer[cnt]; highHalf += lowHalf; } lowHalf %= 65521L; highHalf %= 65521L; result = (highHalf << 16) | lowHalf; return result; } //========================================================================== // InitDriverSupport static long InitDriverSupport( void ) { gExtensionsSpec = (char *) malloc( 4096 ); gDriverSpec = (char *) malloc( 4096 ); gFileSpec = (char *) malloc( 4096 ); gTempSpec = (char *) malloc( 4096 ); gFileName = (char *) malloc( 4096 ); if ( !gExtensionsSpec || !gDriverSpec || !gFileSpec || !gTempSpec || !gFileName ) stop("InitDriverSupport error"); return 0; } //========================================================================== // LoadPrebootDrivers static void LoadPrebootDrivers() { if(gExtensionsSpec == NULL) stop("LoadPrebootDrivers: Unexpected global state\n"); if(gPrebootExtraDir == NULL) { // TODO: We could support preboot kexts from the real root device return; } // Path on ramdisk is /Library/Application Support/DarwinBoot/Extra/ // which should contain Extensions folder and/or Extensions.mkext file // e.g. rd(0)/Library/Application Support/DarwinBoot/Extra/Extensions/ // We name it that way since FileLoadDrivers searches for Extensions.mkext and // Extensions in the given directory and it seems silly to rewrite it to look for // ExtraExtensions or variations thereof. strcpy(gExtensionsSpec, gPrebootExtraDir); FileLoadDrivers(gExtensionsSpec, 0 /* root dir.. not a kext w/ plugins */); } //========================================================================== // LoadDrivers long LoadDrivers( char * dirSpec ) { if ( InitDriverSupport() != 0 ) return 0; // Load any preboot drivers LoadPrebootDrivers(); // Load extra drivers if a hook has been installed. if (LoadExtraDrivers_p != NULL) { long ret = (*LoadExtraDrivers_p)(&FileLoadDrivers); if(ret != 0) return ret; } if ( gBootFileType == kNetworkDeviceType ) { if (NetLoadDrivers(dirSpec) != 0) { error("Could not load drivers from the network\n"); return -1; } } else if ( gBootFileType == kBlockDeviceType ) { if (gMKextName[0] != '\0') { verbose("LoadDrivers: Loading from [%s]\n", gMKextName); if ( LoadDriverMKext(gMKextName) != 0 ) { error("Could not load %s\n", gMKextName); return -1; } } else { strcpy(gExtensionsSpec, dirSpec); strcat(gExtensionsSpec, "System/Library/"); SnowLeopardFileLoadDrivers(gExtensionsSpec); } } else { return 0; } MatchPersonalities(); MatchLibraries(); LoadMatchedModules(); return 0; } #define SNOW_LEOPARD_KEXT_CACHES_DIR "/System/Library/Caches/com.apple.kext.caches/Startup/" static long SnowLeopardFileLoadDrivers( char * dirSpec ) { long ret, flags, time, time2; ret = GetFileInfo(SNOW_LEOPARD_KEXT_CACHES_DIR, "Extensions.mkext", &flags, &time); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeFlat)) { ret = GetFileInfo(dirSpec, "Extensions", &flags, &time2); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeDirectory) || (((gBootMode & kBootModeSafe) == 0) && (time == (time2 + 1)))) { sprintf(gDriverSpec, "%sExtensions.mkext", SNOW_LEOPARD_KEXT_CACHES_DIR); verbose("LoadDrivers: Loading from [%s]\n", gDriverSpec); if (LoadDriverMKext(gDriverSpec) == 0) return 0; } } return FileLoadDrivers(dirSpec, 0); } //========================================================================== // FileLoadDrivers static long FileLoadDrivers( char * dirSpec, long plugin ) { long ret, length, index, flags, time, bundleType; const char * name; if ( !plugin ) { long time2; ret = GetFileInfo(dirSpec, "Extensions.mkext", &flags, &time); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeFlat)) { ret = GetFileInfo(dirSpec, "Extensions", &flags, &time2); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeDirectory) || (((gBootMode & kBootModeSafe) == 0) && (time == (time2 + 1)))) { sprintf(gDriverSpec, "%sExtensions.mkext", dirSpec); verbose("LoadDrivers: Loading from [%s]\n", gDriverSpec); if (LoadDriverMKext(gDriverSpec) == 0) return 0; } } strcat(dirSpec, "Extensions"); } verbose("LoadDrivers: Loading from [%s]\n", dirSpec); index = 0; while (1) { ret = GetDirEntry(dirSpec, &index, &name, &flags, &time); if (ret == -1) break; // Make sure this is a directory. if ((flags & kFileTypeMask) != kFileTypeDirectory) continue; // Make sure this is a kext. length = strlen(name); if (strcmp(name + length - 5, ".kext")) continue; // Save the file name. strcpy(gFileName, name); // Determine the bundle type. sprintf(gTempSpec, "%s/%s", dirSpec, gFileName); ret = GetFileInfo(gTempSpec, "Contents", &flags, &time); if (ret == 0) bundleType = kCFBundleType2; else bundleType = kCFBundleType3; if (!plugin) sprintf(gDriverSpec, "%s/%s/%sPlugIns", dirSpec, gFileName, (bundleType == kCFBundleType2) ? "Contents/" : ""); ret = LoadDriverPList(dirSpec, gFileName, bundleType); if (ret != 0) { //printf("LoadDrivers: failed for '%s'/'%s'\n", dirSpec, gFileName); } if (!plugin) ret = FileLoadDrivers(gDriverSpec, 1); } return 0; } //========================================================================== // static long NetLoadDrivers( char * dirSpec ) { long tries; #if NODEF long cnt; // Get the name of the kernel cnt = strlen(gBootFile); while (cnt--) { if ((gBootFile[cnt] == '\\') || (gBootFile[cnt] == ',')) { cnt++; break; } } #endif // INTEL modification sprintf(gDriverSpec, "%s%s.mkext", dirSpec, bootInfo->bootFile); verbose("NetLoadDrivers: Loading from [%s]\n", gDriverSpec); tries = 3; while (tries--) { if (LoadDriverMKext(gDriverSpec) == 0) break; } if (tries == -1) return -1; return 0; } //========================================================================== // loadDriverMKext static long LoadDriverMKext( char * fileSpec ) { unsigned long driversAddr, driversLength; long length; char segName[32]; DriversPackage * package; #define GetPackageElement(e) OSSwapBigToHostInt32(package->e) // Load the MKext. length = LoadThinFatFile(fileSpec, (void **)&package); if (length == -1) return -1; // Verify the MKext. if (( GetPackageElement(signature1) != kDriverPackageSignature1) || ( GetPackageElement(signature2) != kDriverPackageSignature2) || ( GetPackageElement(length) > kLoadSize ) || ( GetPackageElement(alder32) != Alder32((unsigned char *)&package->version, GetPackageElement(length) - 0x10) ) ) { return -1; } // Make space for the MKext. driversLength = GetPackageElement(length); driversAddr = AllocateKernelMemory(driversLength); // Copy the MKext. memcpy((void *)driversAddr, (void *)package, driversLength); // Add the MKext to the memory map. sprintf(segName, "DriversPackage-%lx", driversAddr); AllocateMemoryRange(segName, driversAddr, driversLength, kBootDriverTypeMKEXT); return 0; } //========================================================================== // LoadDriverPList static long LoadDriverPList( char * dirSpec, char * name, long bundleType ) { long length, driverPathLength; ModulePtr module; TagPtr personalities; char * buffer = 0; char * tmpDriverPath = 0; long ret = -1; do { // Save the driver path. sprintf(gFileSpec, "%s/%s/%s", dirSpec, name, (bundleType == kCFBundleType2) ? "Contents/MacOS/" : ""); driverPathLength = strlen(gFileSpec) + 1; tmpDriverPath = malloc(driverPathLength); if (tmpDriverPath == 0) break; strcpy(tmpDriverPath, gFileSpec); // Construct the file spec to the plist, then load it. sprintf(gFileSpec, "%s/%s/%sInfo.plist", dirSpec, name, (bundleType == kCFBundleType2) ? "Contents/" : ""); length = LoadFile(gFileSpec); if (length == -1) break; length = length + 1; buffer = malloc(length); if (buffer == 0) break; strlcpy(buffer, (char *)kLoadAddr, length); // Parse the plist. ret = ParseXML(buffer, &module, &personalities); if (ret != 0) { break; } // Allocate memory for the driver path and the plist. module->driverPath = tmpDriverPath; module->plistAddr = (void *)malloc(length); if ((module->driverPath == 0) || (module->plistAddr == 0)) break; // Save the driver path in the module. //strcpy(module->driverPath, tmpDriverPath); tmpDriverPath = 0; // Add the plist to the module. strlcpy(module->plistAddr, (char *)kLoadAddr, length); module->plistLength = length; // Add the module to the end of the module list. if (gModuleHead == 0) gModuleHead = module; else gModuleTail->nextModule = module; gModuleTail = module; // Add the persionalities to the personality list. if (personalities) personalities = personalities->tag; while (personalities != 0) { if (gPersonalityHead == 0) gPersonalityHead = personalities->tag; else gPersonalityTail->tagNext = personalities->tag; gPersonalityTail = personalities->tag; personalities = personalities->tagNext; } ret = 0; } while (0); if ( buffer ) free( buffer ); if ( tmpDriverPath ) free( tmpDriverPath ); return ret; } //========================================================================== // LoadMatchedModules static long LoadMatchedModules( void ) { TagPtr prop; ModulePtr module; char *fileName, segName[32]; DriverInfoPtr driver; long length, driverAddr, driverLength; void *driverModuleAddr = 0; module = gModuleHead; while (module != 0) { if (module->willLoad) { prop = XMLGetProperty(module->dict, kPropCFBundleExecutable); if (prop != 0) { fileName = prop->string; sprintf(gFileSpec, "%s%s", module->driverPath, fileName); length = LoadThinFatFile(gFileSpec, &driverModuleAddr); //length = LoadFile(gFileSpec); //driverModuleAddr = (void *)kLoadAddr; //printf("%s length = %d addr = 0x%x\n", gFileSpec, length, driverModuleAddr); getc(); } else length = 0; if (length != -1) { long pathLength = prop ? strlen(gFileSpec) + 1 /* NUL */ : 0; //driverModuleAddr = (void *)kLoadAddr; //if (length != 0) //{ // ThinFatFile(&driverModuleAddr, &length); //} // Make room in the image area. driverLength = sizeof(DriverInfo) + module->plistLength + length + pathLength; driverAddr = AllocateKernelMemory(driverLength); // Set up the DriverInfo. driver = (DriverInfoPtr)driverAddr; driver->plistAddr = (char *)(driverAddr + sizeof(DriverInfo)); driver->plistLength = module->plistLength; if (length != 0) { driver->moduleAddr = (void *)(driverAddr + sizeof(DriverInfo) + module->plistLength); driver->moduleLength = length; } else { driver->moduleAddr = 0; driver->moduleLength = 0; } driver->pathAddr = pathLength ? (char *)(driverAddr + sizeof(DriverInfo) + module->plistLength + length) : NULL; driver->pathLength = pathLength; // Save the plist, module and path. strcpy(driver->plistAddr, module->plistAddr); if (length != 0) { memcpy(driver->moduleAddr, driverModuleAddr, length); } if (pathLength) { strcpy(driver->pathAddr, gFileSpec); } // Add an entry to the memory map. sprintf(segName, "Driver-%lx", (unsigned long)driver); AllocateMemoryRange(segName, driverAddr, driverLength, kBootDriverTypeKEXT); } } module = module->nextModule; } return 0; } //========================================================================== // MatchPersonalities static long MatchPersonalities( void ) { /* IONameMatch support not implemented */ return 0; } //========================================================================== // MatchLibraries static long MatchLibraries( void ) { TagPtr prop, prop2; ModulePtr module, module2; long done; do { done = 1; module = gModuleHead; while (module != 0) { if (module->willLoad == 1) { prop = XMLGetProperty(module->dict, kPropOSBundleLibraries); if (prop != 0) { prop = prop->tag; while (prop != 0) { module2 = gModuleHead; while (module2 != 0) { prop2 = XMLGetProperty(module2->dict, kPropCFBundleIdentifier); if ((prop2 != 0) && (!strcmp(prop->string, prop2->string))) { if (module2->willLoad == 0) module2->willLoad = 1; break; } module2 = module2->nextModule; } prop = prop->tagNext; } } module->willLoad = 2; done = 0; } module = module->nextModule; } } while (!done); return 0; } //========================================================================== // FindModule #if NOTDEF static ModulePtr FindModule( char * name ) { ModulePtr module; TagPtr prop; module = gModuleHead; while (module != 0) { prop = GetProperty(module->dict, kPropCFBundleIdentifier); if ((prop != 0) && !strcmp(name, prop->string)) break; module = module->nextModule; } return module; } #endif /* NOTDEF */ //========================================================================== // ParseXML static long ParseXML( char * buffer, ModulePtr * module, TagPtr * personalities ) { long length, pos; TagPtr moduleDict, required; ModulePtr tmpModule; pos = 0; while (1) { length = XMLParseNextTag(buffer + pos, &moduleDict); if (length == -1) break; pos += length; if (moduleDict == 0) continue; if (moduleDict->type == kTagTypeDict) break; XMLFreeTag(moduleDict); } if (length == -1) return -1; required = XMLGetProperty(moduleDict, kPropOSBundleRequired); if ( (required == 0) || (required->type != kTagTypeString) || !strcmp(required->string, "Safe Boot")) { XMLFreeTag(moduleDict); return -2; } tmpModule = (ModulePtr)malloc(sizeof(Module)); if (tmpModule == 0) { XMLFreeTag(moduleDict); return -1; } tmpModule->dict = moduleDict; // For now, load any module that has OSBundleRequired != "Safe Boot". tmpModule->willLoad = 1; *module = tmpModule; // Get the personalities. *personalities = XMLGetProperty(moduleDict, kPropIOKitPersonalities); return 0; } #if NOTDEF static char gPlatformName[64]; #endif long DecodeKernel(void *binary, entry_t *rentry, char **raddr, int *rsize) { long ret; compressed_kernel_header * kernel_header = (compressed_kernel_header *) binary; u_int32_t uncompressed_size, size; void *buffer; #if 0 printf("kernel header:\n"); printf("signature: 0x%x\n", kernel_header->signature); printf("compress_type: 0x%x\n", kernel_header->compress_type); printf("adler32: 0x%x\n", kernel_header->adler32); printf("uncompressed_size: 0x%x\n", kernel_header->uncompressed_size); printf("compressed_size: 0x%x\n", kernel_header->compressed_size); getc(); #endif if (kernel_header->signature == OSSwapBigToHostConstInt32('comp')) { if (kernel_header->compress_type != OSSwapBigToHostConstInt32('lzss')) { error("kernel compression is bad\n"); return -1; } #if NOTDEF if (kernel_header->platform_name[0] && strcmp(gPlatformName, kernel_header->platform_name)) return -1; if (kernel_header->root_path[0] && strcmp(gBootFile, kernel_header->root_path)) return -1; #endif uncompressed_size = OSSwapBigToHostInt32(kernel_header->uncompressed_size); binary = buffer = malloc(uncompressed_size); size = decompress_lzss((u_int8_t *) binary, &kernel_header->data[0], OSSwapBigToHostInt32(kernel_header->compressed_size)); if (uncompressed_size != size) { error("size mismatch from lzss: %x\n", size); return -1; } if (OSSwapBigToHostInt32(kernel_header->adler32) != Alder32(binary, uncompressed_size)) { printf("adler mismatch\n"); return -1; } } ThinFatFile(&binary, 0); ret = DecodeMachO(binary, rentry, raddr, rsize); return ret; }