/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * 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.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_LICENSE_HEADER_END@ */ /* * main.c - Main functions for BootX. * * Copyright (c) 1998-2003 Apple Computer, Inc. * * DRI: Josh de Cesare */ #include static void Start(void *unused1, void *unused2, ClientInterfacePtr ciPtr); static void Main(ClientInterfacePtr ciPtr); static long InitEverything(ClientInterfacePtr ciPtr); static long DecodeKernel(void); static long SetUpBootArgs(void); static long CallKernel(void); static void FailToBoot(long num); static long InitMemoryMap(void); static long GetOFVersion(void); static long TestForKey(long key); static long GetBootPaths(void); const unsigned long StartTVector[2] = {(unsigned long)Start, 0}; char gStackBaseAddr[0x8000]; char *gVectorSaveAddr; long gImageLastKernelAddr = 0; long gImageFirstBootXAddr = kLoadAddr; long gKernelEntryPoint; long gDeviceTreeAddr; long gDeviceTreeSize; long gBootArgsAddr; long gBootArgsSize; long gSymbolTableAddr; long gSymbolTableSize; long gBootSourceNumber = -1; long gBootSourceNumberMax; long gBootMode = kBootModeNormal; long gBootDeviceType; long gBootFileType; char gHaveKernelCache = 0; char gBootDevice[256]; char gBootFile[256]; static char gBootKernelCacheFile[512]; static char gExtensionsSpec[4096]; static char gCacheNameAdler[64 + sizeof(gBootFile)]; static char *gPlatformName = gCacheNameAdler; char gTempStr[4096]; long *gDeviceTreeMMTmp = 0; long gOFVersion; char *gKeyMap; long gRootAddrCells; long gRootSizeCells; CICell gChosenPH; CICell gOptionsPH; CICell gScreenPH; CICell gMemoryMapPH; CICell gStdOutPH; CICell gMMUIH; CICell gMemoryIH; CICell gStdOutIH; CICell gKeyboardIH; static char gOFVectorSave[kVectorSize]; static unsigned long gOFMSRSave; static unsigned long gOFSPRG0Save; static unsigned long gOFSPRG1Save; static unsigned long gOFSPRG2Save; static unsigned long gOFSPRG3Save; // Private Functions static void Start(void *unused1, void *unused2, ClientInterfacePtr ciPtr) { long newSP; // Move the Stack to a chunk of the BSS newSP = (long)gStackBaseAddr + sizeof(gStackBaseAddr) - 0x100; __asm__ volatile("mr r1, %0" : : "r" (newSP)); Main(ciPtr); } static void Main(ClientInterfacePtr ciPtr) { long ret; int trycache; long flags, cachetime, time; ret = InitEverything(ciPtr); if (ret != 0) Exit(); // Get or infer the boot paths. ret = GetBootPaths(); if (ret != 0) FailToBoot(1); DrawSplashScreen(0); while (ret == 0) { trycache = (0 == (gBootMode & kBootModeSafe)) && (gBootKernelCacheFile[0] != 0); if (trycache && (gBootFileType == kBlockDeviceType)) do { // if we haven't found the kernel yet, don't use the cache ret = GetFileInfo(NULL, gBootFile, &flags, &time); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) { trycache = 0; break; } ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat) || (cachetime < time)) { trycache = 0; break; } ret = GetFileInfo(gExtensionsSpec, "Extensions", &flags, &time); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory) && (cachetime < time)) { trycache = 0; break; } } while (0); if (trycache) { ret = LoadFile(gBootKernelCacheFile); if (ret != -1) { ret = DecodeKernel(); if (ret != -1) break; } } ret = LoadFile(gBootFile); if (ret != -1) ret = DecodeKernel(); if (ret != -1) break; ret = GetBootPaths(); if (ret != 0) FailToBoot(2); } if (ret != 0) FailToBoot(3); if (!gHaveKernelCache) { ret = LoadDrivers(gExtensionsSpec); if (ret != 0) FailToBoot(4); } DrawSplashScreen(1); ret = SetUpBootArgs(); if (ret != 0) FailToBoot(5); ret = CallKernel(); FailToBoot(6); } static long InitEverything(ClientInterfacePtr ciPtr) { long ret, mem_base, mem_base2, size; CICell keyboardPH; char name[32], securityMode[33]; long length; char *compatible; // Init the OF Client Interface. ret = InitCI(ciPtr); if (ret != 0) return -1; // Get the OF Version gOFVersion = GetOFVersion(); if (gOFVersion == 0) return -1; // Get the address and size cells for the root. GetProp(Peer(0), "#address-cells", (char *)&gRootAddrCells, 4); GetProp(Peer(0), "#size-cells", (char *)&gRootSizeCells, 4); if ((gRootAddrCells > 2) || (gRootAddrCells > 2)) return -1; // Init the SL Words package. ret = InitSLWords(); if (ret != 0) return -1; // Get the phandle for /options gOptionsPH = FindDevice("/options"); if (gOptionsPH == -1) return -1; // Get the phandle for /chosen gChosenPH = FindDevice("/chosen"); if (gChosenPH == -1) return -1; // Init the Memory Map. ret = InitMemoryMap(); if (ret != 0) return -1; // Get IHandles for the MMU and Memory size = GetProp(gChosenPH, "mmu", (char *)&gMMUIH, 4); if (size != 4) { printf("Failed to get the IH for the MMU.\n"); return -1; } size = GetProp(gChosenPH, "memory", (char *)&gMemoryIH, 4); if (size != 4) { printf("Failed to get the IH for the Memory.\n"); return -1; } // Get first element of root's compatible property. ret = GetPackageProperty(Peer(0), "compatible", &compatible, &length); if (ret != -1) strcpy(gPlatformName, compatible); // Get stdout's IH, so that the boot display can be found. ret = GetProp(gChosenPH, "stdout", (char *)&gStdOutIH, 4); if (ret == 4) gStdOutPH = InstanceToPackage(gStdOutIH); else gStdOutPH = gStdOutIH = 0; // Try to find the keyboard using chosen ret = GetProp(gChosenPH, "stdin", (char *)&gKeyboardIH, 4); if (ret != 4) gKeyboardIH = 0; else { keyboardPH = InstanceToPackage(gKeyboardIH); ret = GetProp(keyboardPH, "name", name, 31); if (ret != -1) { name[ret] = '\0'; if (strcmp(name, "keyboard") && strcmp(name, "kbd")) gKeyboardIH = 0; } else gKeyboardIH = 0; } // Try to the find the keyboard using open if chosen did not work. if (gKeyboardIH == 0) gKeyboardIH = Open("keyboard"); if (gKeyboardIH == 0) gKeyboardIH = Open("kbd"); // Get the key map set up, and make it up to date. gKeyMap = InitKeyMap(gKeyboardIH); if (gKeyMap == NULL) return -1; UpdateKeyMap(); // Test for Secure Boot Mode. size = GetProp(gOptionsPH, "security-mode", securityMode, 32); if (size != -1) { securityMode[size] = '\0'; if (strcmp(securityMode, "none")) gBootMode |= kBootModeSecure; } #if kFailToBoot // 'cmd-s' or 'cmd-v' is pressed set outputLevel to kOutputLevelFull if (((gBootMode & kBootModeSecure) == 0) && TestForKey(kCommandKey) && (TestForKey('s') || TestForKey('v'))) { SetOutputLevel(kOutputLevelFull); } else { SetOutputLevel(kOutputLevelOff); } #else SetOutputLevel(kOutputLevelFull); #endif // printf now works. printf("\n\nMac OS X Loader\n"); // Test for Safe Boot Mode. if (((gBootMode & kBootModeSecure) == 0) && TestForKey(kShiftKey)) { gBootMode |= kBootModeSafe; } // Claim memory for the FS Cache. if (Claim(kFSCacheAddr, kFSCacheSize, 0) == 0) { printf("Claim for fs cache failed.\n"); return -1; } // Claim memory for malloc. if (Claim(kMallocAddr, kMallocSize, 0) == 0) { printf("Claim for malloc failed.\n"); return -1; } malloc_init((char *)kMallocAddr, kMallocSize); // Claim memory for the Load Addr. mem_base = Claim(kLoadAddr, kLoadSize, 0); if (mem_base == 0) { printf("Claim for Load Area failed.\n"); return -1; } // Claim the memory for the Image Addr if (gOFVersion >= kOFVersion3x) { mem_base = Claim(kImageAddr, kImageSize, 0); if (mem_base == 0) { printf("Claim for Image Area failed.\n"); return -1; } } else { // Claim the 1:1 mapped chunks first. mem_base = Claim(kImageAddr0, kImageSize0, 0); mem_base2 = Claim(kImageAddr2, kImageSize2, 0); if ((mem_base == 0) || (mem_base2 == 0)) { printf("Claim for Image Area failed.\n"); return -1; } // Unmap the old xcoff stack. CallMethod(2, 0, gMMUIH, "unmap", 0x00380000, 0x00080000); // Grap the physical memory then the logical. CallMethod(3, 1, gMemoryIH, "claim", kImageAddr1Phys, kImageSize1, 0, &mem_base); CallMethod(3, 1, gMMUIH, "claim", kImageAddr1, kImageSize1, 0, &mem_base2); if ((mem_base == 0) || (mem_base2 == 0)) { printf("Claim for Image Area failed.\n"); return -1; } // Map them together. CallMethod(4, 0, gMMUIH, "map", kImageAddr1Phys, kImageAddr1, kImageSize1, 0); } bzero((char *)kImageAddr, kImageSize); // Allocate some space for the Vector Save area. gVectorSaveAddr = AllocateBootXMemory(kVectorSize); if (gVectorSaveAddr == 0) { printf("Allocation for the Vector Save Area failed.\n"); return -1; } // Find all the displays and set them up. ret = InitDisplays(); if (ret != 0) { printf("InitDisplays failed.\n"); return -1; } return 0; } long ThinFatBinary(void **binary, unsigned long *length) { long ret; ret = ThinFatBinaryMachO(binary, length); if (ret == -1) ret = ThinFatBinaryElf(binary, length); return ret; } static long DecodeKernel(void) { void *binary = (void *)kLoadAddr; long ret; compressed_kernel_header * kernel_header = (compressed_kernel_header *) kLoadAddr; u_int32_t size; if (kernel_header->signature == 'comp') { if (kernel_header->compress_type != 'lzss') return -1; 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; binary = AllocateBootXMemory(kernel_header->uncompressed_size); size = decompress_lzss((u_int8_t *) binary, &kernel_header->data[0], kernel_header->compressed_size); if (kernel_header->uncompressed_size != size) { printf("size mismatch from lzss %x\n", size); return -1; } if (kernel_header->adler32 != Alder32(binary, kernel_header->uncompressed_size)) { printf("adler mismatch\n"); return -1; } } ThinFatBinary(&binary, 0); ret = DecodeMachO(binary); if (ret == -1) ret = DecodeElf(binary); return ret; } static long SetUpBootArgs(void) { boot_args_ptr args; CICell memoryPH; long graphicsBoot = 1; long ret, cnt, size, dash; long sKey, vKey, keyPos; char ofBootArgs[128], *ofArgs, tc, keyStr[8]; unsigned char mem_regs[kMaxDRAMBanks*16]; unsigned long mem_banks, bank_shift; // Save file system cache statistics. SetProp(gChosenPH, "BootXCacheHits", (char *)&gCacheHits, 4); SetProp(gChosenPH, "BootXCacheMisses", (char *)&gCacheMisses, 4); SetProp(gChosenPH, "BootXCacheEvicts", (char *)&gCacheEvicts, 4); // Allocate some memory for the BootArgs. gBootArgsSize = sizeof(boot_args); gBootArgsAddr = AllocateKernelMemory(gBootArgsSize); // Add the BootArgs to the memory-map. AllocateMemoryRange("BootArgs", gBootArgsAddr, gBootArgsSize); args = (boot_args_ptr)gBootArgsAddr; args->Revision = kBootArgsRevision; args->Version = kBootArgsVersion1; args->machineType = 0; // Check the Keyboard for 'cmd-s' and 'cmd-v' UpdateKeyMap(); if ((gBootMode & kBootModeSecure) == 0) { sKey = TestForKey(kCommandKey) && TestForKey('s'); vKey = TestForKey(kCommandKey) && TestForKey('v'); } else { sKey = 0; vKey = 0; } // if 'cmd-s' or 'cmd-v' was pressed do a text boot. if (sKey || vKey) graphicsBoot = 0; // Create the command line. if (gOFVersion < kOFVersion3x) { ofBootArgs[0] = ' '; size = GetProp(gChosenPH, "machargs", ofBootArgs + 1, 126); if (size == -1) { size = GetProp(gOptionsPH, "boot-command", ofBootArgs, 127); if (size == -1) ofBootArgs[0] = '\0'; else ofBootArgs[size] = '\0'; // Look for " bootr" but skip the number. if (!strncmp(ofBootArgs + 1, " bootr", 6)) { strcpy(ofBootArgs, ofBootArgs + 7); } else ofBootArgs[0] = '\0'; SetProp(gChosenPH, "machargs", ofBootArgs, strlen(ofBootArgs) + 1); } else ofBootArgs[size] = '\0'; // Force boot-command to start with 0 bootr. sprintf(gTempStr, "0 bootr%s", ofBootArgs); SetProp(gOptionsPH, "boot-command", gTempStr, strlen(gTempStr)); } else { size = GetProp(gOptionsPH, "boot-args", ofBootArgs, 127); if (size == -1) ofBootArgs[0] = '\0'; else ofBootArgs[size] = '\0'; } if (ofBootArgs[0] != '\0') { // Look for special options and copy the rest. dash = 0; ofArgs = ofBootArgs; while ((tc = *ofArgs) != '\0') { tc = tolower(tc); // Check for entering a dash arg. if (tc == '-') { dash = 1; ofArgs++; continue; } // Do special stuff if in a dash arg. if (dash) { if (tc == 's') { graphicsBoot = 0; ofArgs++; sKey = 0; } else if (tc == 'v') { graphicsBoot = 0; ofArgs++; vKey = 0; } else { // Check for exiting dash arg if (isspace(tc)) dash = 0; // Copy any non 's' or 'v' ofArgs++; } } else { // Not a dash arg so just copy it. ofArgs++; } } } // Add any pressed keys (s, v, shift) to the command line keyPos = 0; if (sKey || vKey || (gBootMode & kBootModeSafe)) { keyStr[keyPos++] = '-'; if (sKey) keyStr[keyPos++] = 's'; if (vKey) keyStr[keyPos++] = 'v'; if (gBootMode & kBootModeSafe) keyStr[keyPos++] = 'x'; keyStr[keyPos++] = ' '; } keyStr[keyPos++] = '\0'; sprintf(args->CommandLine, "%s%s", keyStr, ofBootArgs); // If the address or size cells are larger than 1, use page numbers // and signify Boot Args Version 2. if ((gRootAddrCells == 1) && (gRootSizeCells == 1)) bank_shift = 0; else { bank_shift = 12; args->Version = kBootArgsVersion2; } // Get the information about the memory banks memoryPH = FindDevice("/memory"); if (memoryPH == -1) return -1; size = GetProp(memoryPH, "reg", mem_regs, kMaxDRAMBanks * 16); if (size == 0) return -1; mem_banks = size / (4 * (gRootAddrCells + gRootSizeCells)); if (mem_banks > kMaxDRAMBanks) mem_banks = kMaxDRAMBanks; // Convert the reg properties to 32 bit values for (cnt = 0; cnt < mem_banks; cnt++) { if (gRootAddrCells == 1) { args->PhysicalDRAM[cnt].base = *(unsigned long *)(mem_regs + cnt * 4 * (gRootAddrCells + gRootSizeCells)) >> bank_shift; } else { args->PhysicalDRAM[cnt].base = *(unsigned long long *)(mem_regs + cnt * 4 * (gRootAddrCells + gRootSizeCells)) >> bank_shift; } if (gRootSizeCells == 1) { args->PhysicalDRAM[cnt].size = *(unsigned long *)(mem_regs + cnt * 4 * (gRootAddrCells + gRootSizeCells) + 4 * gRootAddrCells) >> bank_shift; } else { args->PhysicalDRAM[cnt].size = *(unsigned long long *)(mem_regs + cnt * 4 * (gRootAddrCells + gRootSizeCells) + 4 * gRootAddrCells) >> bank_shift; } } // Collapse the memory banks into contiguous chunks for (cnt = 0; cnt < mem_banks - 1; cnt++) { if ((args->PhysicalDRAM[cnt + 1].base != 0) && ((args->PhysicalDRAM[cnt].base + args->PhysicalDRAM[cnt].size) != args->PhysicalDRAM[cnt + 1].base)) continue; args->PhysicalDRAM[cnt].size += args->PhysicalDRAM[cnt + 1].size; bcopy(args->PhysicalDRAM + cnt + 2, args->PhysicalDRAM + cnt + 1, (mem_banks - cnt - 2) * sizeof(DRAMBank)); mem_banks--; cnt--; } bzero(args->PhysicalDRAM + mem_banks, (kMaxDRAMBanks - mem_banks) * sizeof(DRAMBank)); // Get the video info GetMainScreenPH(&args->Video); args->Video.v_display = graphicsBoot; // Add the DeviceTree to the memory-map. // The actuall address and size must be filled in later. AllocateMemoryRange("DeviceTree", 0, 0); ret = FlattenDeviceTree(); if (ret != 0) return -1; // Fill in the address and size of the device tree. if (gDeviceTreeAddr) { gDeviceTreeMMTmp[0] = gDeviceTreeAddr; gDeviceTreeMMTmp[1] = gDeviceTreeSize; } args->deviceTreeP = (void *)gDeviceTreeAddr; args->deviceTreeLength = gDeviceTreeSize; args->topOfKernelData = AllocateKernelMemory(0); return 0; } static long CallKernel(void) { unsigned long msr, cnt; Quiesce(); printf("\nCall Kernel!\n"); // Save SPRs for OF __asm__ volatile("mfmsr %0" : "=r" (gOFMSRSave)); __asm__ volatile("mfsprg %0, 0" : "=r" (gOFSPRG0Save)); __asm__ volatile("mfsprg %0, 1" : "=r" (gOFSPRG1Save)); __asm__ volatile("mfsprg %0, 2" : "=r" (gOFSPRG2Save)); __asm__ volatile("mfsprg %0, 3" : "=r" (gOFSPRG3Save)); // Turn off translations msr = 0x00001000; __asm__ volatile("sync"); __asm__ volatile("mtmsr %0" : : "r" (msr)); __asm__ volatile("isync"); // Save the OF's Exceptions Vectors bcopy(0x0, gOFVectorSave, kVectorSize); // Move the Exception Vectors bcopy(gVectorSaveAddr, 0x0, kVectorSize); for (cnt = 0; cnt < kVectorSize; cnt += 0x20) { __asm__ volatile("dcbf 0, %0" : : "r" (cnt)); __asm__ volatile("icbi 0, %0" : : "r" (cnt)); } // Move the Image1 save area for OF 1.x / 2.x if (gOFVersion < kOFVersion3x) { bcopy((char *)kImageAddr1Phys, (char *)kImageAddr1, kImageSize1); for (cnt = kImageAddr1; cnt < kImageSize1; cnt += 0x20) { __asm__ volatile("dcbf 0, %0" : : "r" (cnt)); __asm__ volatile("icbi 0, %0" : : "r" (cnt)); } } // Make sure everything get sync'd up. __asm__ volatile("isync"); __asm__ volatile("sync"); __asm__ volatile("eieio"); // Call the Kernel's entry point (*(void (*)())gKernelEntryPoint)(gBootArgsAddr, kMacOSXSignature); // Restore OF's Exception Vectors bcopy(gOFVectorSave, 0x0, 0x3000); for (cnt = 0; cnt < kVectorSize; cnt += 0x20) { __asm__ volatile("dcbf 0, %0" : : "r" (cnt)); __asm__ volatile("icbi 0, %0" : : "r" (cnt)); } // Restore SPRs for OF __asm__ volatile("mtsprg 0, %0" : : "r" (gOFSPRG0Save)); __asm__ volatile("mtsprg 1, %0" : : "r" (gOFSPRG1Save)); __asm__ volatile("mtsprg 2, %0" : : "r" (gOFSPRG2Save)); __asm__ volatile("mtsprg 3, %0" : : "r" (gOFSPRG3Save)); // Restore translations __asm__ volatile("sync"); __asm__ volatile("mtmsr %0" : : "r" (gOFMSRSave)); __asm__ volatile("isync"); return -1; } static void FailToBoot(long num) { #if kFailToBoot DrawFailedBootPicture(); while (1); num = 0; #else printf("FailToBoot: %d\n", num); Enter(); // For debugging #endif } static long InitMemoryMap(void) { long result; result = Interpret(0, 1, " dev /chosen" " new-device" " \" memory-map\" device-name" " active-package" " device-end" , &gMemoryMapPH); return result; } static long GetOFVersion(void) { CICell ph; char versStr[256], *tmpStr; long vers, size; // Get the openprom package ph = FindDevice("/openprom"); if (ph == -1) return 0; // Get it's model property size = GetProp(ph, "model", versStr, 255); if (size == -1) return -1; versStr[size] = '\0'; // Find the start of the number. tmpStr = NULL; if (!strncmp(versStr, "Open Firmware, ", 15)) { tmpStr = versStr + 15; } else if (!strncmp(versStr, "OpenFirmware ", 13)) { tmpStr = versStr + 13; } else return -1; // Clasify by each instance as needed... switch (*tmpStr) { case '1' : vers = kOFVersion1x; break; case '2' : vers = kOFVersion2x; break; case '3' : vers = kOFVersion3x; break; case '4' : vers = kOFVersion4x; break; default : vers = 0; break; } return vers; } static long TestForKey(long key) { long keyNum; long bp; char tc; if (gOFVersion < kOFVersion3x) { switch(key) { case 'a' : keyNum = 7; break; case 's' : keyNum = 6; break; case 'v' : keyNum = 14; break; case 'y' : keyNum = 23; break; case kCommandKey : keyNum = 48; break; case kOptKey : keyNum = 61; break; case kShiftKey : keyNum = 63; break; case kControlKey : keyNum = 49; break; default : keyNum = -1; break; } } else { switch(key) { case 'a' : keyNum = 3; break; case 's' : keyNum = 17; break; case 'v' : keyNum = 30; break; case 'y' : keyNum = 27; break; case kCommandKey : keyNum = 228; break; case kOptKey : keyNum = 229; break; case kShiftKey : keyNum = 230; break; case kControlKey : keyNum = 231; break; default : keyNum = -1; break; } // Map the right modifier keys on to the left. gKeyMap[28] |= gKeyMap[28] << 4; } if (keyNum == -1) return 0; bp = keyNum & 7; tc = gKeyMap[keyNum >> 3]; return (tc & (1 << bp)) != 0; } #define kBootpBootFileOffset (108) static long GetBootPaths(void) { long ret, cnt, cnt2, cnt3, cnt4, size, partNum, bootplen, bsdplen; unsigned long adler32; char *filePath, *buffer; if (gBootSourceNumber == -1) { // Get the boot-device size = GetProp(gChosenPH, "bootpath", gBootDevice, 255); gBootDevice[size] = '\0'; if (gBootDevice[0] == '\0') { size = GetProp(gOptionsPH, "boot-device", gBootDevice, 255); gBootDevice[size] = '\0'; } gBootDeviceType = GetDeviceType(gBootDevice); // Get the boot-file size = GetProp(gChosenPH, "bootargs", gBootFile, 256); gBootFile[size] = '\0'; if (gBootFile[0] != '\0') { gBootFileType = GetDeviceType(gBootFile); gBootSourceNumberMax = 0; } else { gBootSourceNumber = 0; gBootFileType = gBootDeviceType; if (gBootFileType == kNetworkDeviceType) gBootSourceNumberMax = 1; else { if (gOFVersion < kOFVersion3x) { gBootSourceNumberMax = 4; } else { gBootSourceNumberMax = 6; } } } if (gBootFileType == kNetworkDeviceType) { SetProp(Peer(0), "net-boot", NULL, 0); } } if (gBootSourceNumber >= gBootSourceNumberMax) return -1; if (gBootSourceNumberMax != 0) { switch (gBootFileType) { case kNetworkDeviceType : // Find the end of the device spec. cnt = 0; while (gBootDevice[cnt] != ':') cnt++; // Copy the device spec with the ':'. strncpy(gBootFile, gBootDevice, cnt + 1); // Check for bootp-responce or bsdp-responce. bootplen = GetPropLen(gChosenPH, "bootp-response"); bsdplen = GetPropLen(gChosenPH, "bsdp-response"); if ((bootplen > 0) || (bsdplen > 0)) { if (bootplen > 0) { buffer = malloc(bootplen); GetProp(gChosenPH, "bootp-response", buffer, bootplen); } else { buffer = malloc(bsdplen); GetProp(gChosenPH, "bsdp-response", buffer, bsdplen); } // Flip the slash's to back slash's while looking for the last one. cnt = cnt2 = kBootpBootFileOffset; while (buffer[cnt] != '\0') { if (buffer[cnt] == '/') { buffer[cnt] = '\\'; cnt2 = cnt + 1; } cnt++; } // Add a comma at the front. buffer[kBootpBootFileOffset - 1] = ','; // Append the the root dir to the device spec. strncat(gBootFile, buffer + kBootpBootFileOffset - 1, cnt2 - kBootpBootFileOffset + 1); free(buffer); } else { // Look for the start of the root dir path. cnt3 = cnt; while (gBootDevice[cnt3] != ',') cnt3++; // Find the end of the path. Look for a comma or null. cnt2 = cnt3 + 1; while ((gBootDevice[cnt2] != '\0') && (gBootDevice[cnt2] != ',')) cnt2++; // Find the last back slash or comma in the path cnt4 = cnt2 - 1; while ((gBootDevice[cnt4] != ',') && (gBootDevice[cnt4] != '\\')) cnt4--; // Copy the IP addresses if needed. if (gOFVersion < kOFVersion3x) { strncat(gBootFile, gBootDevice + cnt + 1, cnt3 - cnt - 1); } // Add on the directory path strncat(gBootFile, gBootDevice + cnt3, cnt4 - cnt3 + 1); } // Add on the kernel name strcat(gBootFile, "mach.macosx"); // Add on postfix strcat(gBootFile, gBootDevice + cnt2); break; case kBlockDeviceType : // Find the first ':'. cnt = 0; while ((gBootDevice[cnt] != '\0') && (gBootDevice[cnt] != ':')) cnt++; if (gBootDevice[cnt] == '\0') return -1; // Find the comma after the ':'. cnt2 = cnt + 1; while ((gBootDevice[cnt2] != '\0') && (gBootDevice[cnt] != ',')) cnt2++; // Get just the partition number strncpy(gBootFile, gBootDevice + cnt + 1, cnt2 - cnt - 1); partNum = atoi(gBootFile); // Adjust the partition number. // Pass 0 & 1, no offset. Pass 2 & 3, offset 1, Pass 4 & 5, offset 2. partNum += gBootSourceNumber / 2; // Construct the boot-file strncpy(gBootFile, gBootDevice, cnt + 1); sprintf(gBootFile + cnt + 1, "%d,%s\\mach_kernel", partNum, ((gBootSourceNumber & 1) ? "" : "\\")); // and the cache file name bzero(gCacheNameAdler + 64, sizeof(gBootFile)); strcpy(gCacheNameAdler + 64, gBootFile); adler32 = Alder32(gCacheNameAdler, sizeof(gCacheNameAdler)); strncpy(gBootKernelCacheFile, gBootDevice, cnt + 1); sprintf(gBootKernelCacheFile + cnt + 1, "%d,\\System\\Library\\Caches\\com.apple.kernelcaches\\kernelcache.%08lX", partNum, adler32); break; default: printf("Failed to infer Boot Device Type.\n"); return -1; break; } } // Figure out the root dir. ret = ConvertFileSpec(gBootFile, gExtensionsSpec, &filePath); if (ret == -1) return -1; strcat(gExtensionsSpec, ","); // Add in any extra path to gRootDir. cnt = 0; while (filePath[cnt] != '\0') cnt++; if (cnt != 0) { for (cnt2 = cnt - 1; cnt2 >= 0; cnt2--) { if (filePath[cnt2] == '\\') { strncat(gExtensionsSpec, filePath, cnt2 + 1); break; } } } // Figure out the extensions dir. if (gBootFileType == kBlockDeviceType) { cnt = strlen(gExtensionsSpec); if ((cnt > 2) && (gExtensionsSpec[cnt-1] == '\\') && (gExtensionsSpec[cnt-2] == '\\')) cnt--; strcpy(gExtensionsSpec + cnt, "System\\Library\\"); } SetProp(gChosenPH, "rootpath", gBootFile, strlen(gBootFile) + 1); gBootSourceNumber++; return 0; } // Public Functions long GetDeviceType(char *devSpec) { CICell ph; long size; char deviceType[32]; ph = FindDevice(devSpec); if (ph == -1) return -1; size = GetProp(ph, "device_type", deviceType, 31); if (size != -1) deviceType[size] = '\0'; else deviceType[0] = '\0'; if (strcmp(deviceType, "network") == 0) return kNetworkDeviceType; if (strcmp(deviceType, "block") == 0) return kBlockDeviceType; return kUnknownDeviceType; } long ConvertFileSpec(char *fileSpec, char *devSpec, char **filePath) { long cnt; // Find the first ':' in the fileSpec. cnt = 0; while ((fileSpec[cnt] != '\0') && (fileSpec[cnt] != ':')) cnt++; if (fileSpec[cnt] == '\0') return -1; // Find the next ',' in the fileSpec. while ((fileSpec[cnt] != '\0') && (fileSpec[cnt] != ',')) cnt++; // Copy the string to devSpec. strncpy(devSpec, fileSpec, cnt); devSpec[cnt] = '\0'; // If there is a filePath start it after the ',', otherwise NULL. if (filePath != NULL) { if (fileSpec[cnt] != '\0') { *filePath = fileSpec + cnt + 1; } else { *filePath = NULL; } } return 0; } long MatchThis(CICell phandle, char *string) { long ret, length; char *name, *model, *compatible; ret = GetPackageProperty(phandle, "name", &name, &length); if ((ret == -1) || (length == 0)) name = NULL; ret = GetPackageProperty(phandle, "model", &model, &length); if ((ret == -1) || (length == 0)) model = NULL; ret = GetPackageProperty(phandle, "compatible", &compatible, &length); if ((ret == -1) || (length == 0)) model = NULL; if ((name != NULL) && strcmp(name, string) == 0) return 0; if ((model != NULL) && strcmp(model, string) == 0) return 0; if (compatible != NULL) { while (*compatible != '\0') { if (strcmp(compatible, string) == 0) return 0; compatible += strlen(compatible) + 1; } } return -1; } void *AllocateBootXMemory(long size) { long addr = gImageFirstBootXAddr - size; if (addr < gImageLastKernelAddr) return 0; gImageFirstBootXAddr = addr; return (void *)addr; } long AllocateKernelMemory(long size) { long addr = gImageLastKernelAddr; gImageLastKernelAddr += (size + 0xFFF) & ~0xFFF; if (gImageLastKernelAddr > gImageFirstBootXAddr) FailToBoot(-1); return addr; } long AllocateMemoryRange(char *rangeName, long start, long length) { long result, *buffer; buffer = AllocateBootXMemory(2 * sizeof(long)); if (buffer == 0) return -1; buffer[0] = start; buffer[1] = length; result = SetProp(gMemoryMapPH, rangeName, (char *)buffer, 2 * sizeof(long)); if (result == -1) return -1; return 0; } 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; }