int getBootOptions(bool firstRun) { int i; int key; int nextRow; int timeout; int bvCount; BVRef bvr; BVRef menuBVR; bool showPrompt, newShowPrompt, isCDROM; // Initialize default menu selection entry. gBootVolume = menuBVR = selectBootVolume(bvChain); if (biosDevIsCDROM(gBIOSDev)) { isCDROM = true; } else { isCDROM = false; } // ensure we're in graphics mode if gui is setup if (gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE) { setVideoMode(GRAPHICS_MODE, 0); } // Clear command line boot arguments clearBootArgs(); // Allow user to override default timeout. if (multiboot_timeout_set) { timeout = multiboot_timeout; } else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->bootConfig)) { /* If there is no timeout key in the file use the default timeout which is different for CDs vs. hard disks. However, if not booting a CD and no config file could be loaded set the timeout to zero which causes the menu to display immediately. This way, if no partitions can be found, that is the disk is unpartitioned or simply cannot be read) then an empty menu is displayed. If some partitions are found, for example a Windows partition, then these will be displayed in the menu as foreign partitions. */ if (isCDROM) { timeout = kCDBootTimeout; } else { timeout = sysConfigValid ? kBootTimeout : 0; } } if (timeout < 0) { gBootMode |= kBootModeQuiet; } // If the user is holding down a modifier key, enter safe mode. if ((readKeyboardShiftFlags() & 0x0F) != 0) { gBootMode |= kBootModeSafe; } // 18seven's Quick-args macro bool f8 = false, altf = false, shiftf = false, alts = false, altv = false, altx = false; // x32 = false, x64 = false; while (readKeyboardStatus()) { key = bgetc (); if (key == 0x4200) f8 = true; if (key == 0x2100) altf = true; if (key == 0x0046) shiftf = true; if (key == 0x1F00) alts = true; if (key == 0x2F00) altv = true; if (key == 0x2D00) altx = true; /* if (key == 0x0004) x32 = true; if (key == 0x0007) x64 = true; */ } // If user typed F8, abort quiet mode, and display the menu. if (f8) { gBootMode &= ~kBootModeQuiet; timeout = 0; } // If user typed 'alt-v', boot in verbose mode. if ((gBootMode & kBootModeQuiet) && firstRun && altv) { addBootArg(kVerboseModeFlag); } // If user typed 'alt-s', boot in single user mode. if ((gBootMode & kBootModeQuiet) && firstRun && alts) { addBootArg(kSingleUserModeFlag); } if ((gBootMode & kBootModeQuiet) && firstRun && altf) { addBootArg(kIgnoreCachesFlag); } if ((gBootMode & kBootModeQuiet) && firstRun && shiftf) { addBootArg(kIgnoreBootFileFlag); } if ((gBootMode & kBootModeQuiet) && firstRun && altx) { addBootArg(kSafeModeFlag); } /* if ((gBootMode & kBootModeQuiet) && firstRun && x32) { addBootArg(k32BitModeFlag); } if ((gBootMode & kBootModeQuiet) && firstRun && x64) { addBootArg(k64BitModeFlag); } */ if (bootArgs->Video.v_display == VGA_TEXT_MODE) { setCursorPosition(0, 0, 0); clearScreenRows(0, kScreenLastRow); if (!(gBootMode & kBootModeQuiet)) { // Display banner and show hardware info. printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024); printf(getVBEInfoString()); } changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0); verbose("Scanning device %x...", gBIOSDev); } // When booting from CD, default to hard drive boot when possible. if (isCDROM && firstRun) { const char *val; char *prompt = NULL; char *name = NULL; int cnt; int optionKey; if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->bootConfig)) { prompt = malloc(cnt + 1); strncat(prompt, val, cnt); } else { name = malloc(80); getBootVolumeDescription(gBootVolume, name, 79, false); prompt = malloc(256); sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name); free(name); } if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->bootConfig )) { // The key specified is a special key. } else { // Default to F8. optionKey = 0x4200; } // If the timeout is zero then it must have been set above due to the // early catch of F8 which means the user wants to set boot options // which we ought to interpret as meaning he wants to boot the CD. if (timeout != 0) { key = countdown(prompt, kMenuTopRow, timeout); } else { key = optionKey; } if (prompt != NULL) { free(prompt); } clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); // Hit the option key ? if (key == optionKey) { gBootMode &= ~kBootModeQuiet; timeout = 0; } else { key = key & 0xFF; // Try booting hard disk if user pressed 'h' if (biosDevIsCDROM(gBIOSDev) && key == 'h') { BVRef bvr; // Look at partitions hosting OS X other than the CD-ROM for (bvr = bvChain; bvr; bvr=bvr->next) { if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) { gBootVolume = bvr; } } } goto done; } } if (gBootMode & kBootModeQuiet) { // No input allowed from user. goto done; } if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) { // If the user is holding down a modifier key, // enter safe mode. if ((readKeyboardShiftFlags() & 0x0F) != 0) { gBootMode |= kBootModeSafe; } goto done; } if (gDeviceCount) { // Allocate memory for an array of menu items. menuItems = malloc(sizeof(MenuItem) * gDeviceCount); if (menuItems == NULL) { goto done; } // Associate a menu item for each BVRef. for (bvr=bvChain, i=gDeviceCount-1, selectIndex=0; bvr; bvr=bvr->next) { if (bvr->visible) { getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true); menuItems[i].param = (void *) bvr; if (bvr == menuBVR) { selectIndex = i; } i--; } } } if (bootArgs->Video.v_display == GRAPHICS_MODE) { // redraw the background buffer gui.logo.draw = true; drawBackground(); gui.devicelist.draw = true; gui.redraw = true; if (!(gBootMode & kBootModeQuiet)) { bool showBootBanner = true; // Check if "Boot Banner"=N switch is present in config file. getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->bootConfig); if (showBootBanner) { // Display banner and show hardware info. gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024); } // redraw background memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4); } } else { // Clear screen and hide the blinking cursor. clearScreenRows(kMenuTopRow, kMenuTopRow + 2); changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0); } nextRow = kMenuTopRow; showPrompt = true; if (gDeviceCount) { if( bootArgs->Video.v_display == VGA_TEXT_MODE ) { printf("Use \30\31 keys to select the startup volume."); } showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); nextRow += min( gDeviceCount, kMenuMaxItems ) + 3; } // Show the boot prompt. showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt( nextRow, showPrompt ); do { if (bootArgs->Video.v_display == GRAPHICS_MODE) { // redraw background memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 ); // reset cursor co-ords gui.debug.cursor = pos( gui.screen.width - 160 , 10 ); } key = getc(); updateMenu( key, (void **) &menuBVR ); newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); if (newShowPrompt != showPrompt) { showPrompt = newShowPrompt; showBootPrompt( nextRow, showPrompt ); } if (showPrompt) { updateBootArgs(key); } switch (key) { case kReturnKey: if (gui.menu.draw) { key=0; break; } if (*gBootArgs == '?') { char * argPtr = gBootArgs; // Skip the leading "?" character. argPtr++; getNextArg(&argPtr, booterCommand); getNextArg(&argPtr, booterParam); /* * TODO: this needs to be refactored. */ if (strcmp( booterCommand, "video" ) == 0) { if (bootArgs->Video.v_display == GRAPHICS_MODE) { showInfoBox(getVBEInfoString(), getVBEModeInfoString()); } else { printVBEModeInfo(); } } else if ( strcmp( booterCommand, "memory" ) == 0) { if (bootArgs->Video.v_display == GRAPHICS_MODE ) { showInfoBox("Memory Map", getMemoryInfoString()); } else { printMemoryInfo(); } } else if (strcmp(booterCommand, "lspci") == 0) { lspci(); } else if (strcmp(booterCommand, "more") == 0) { showTextFile(booterParam); } else if (strcmp(booterCommand, "rd") == 0) { processRAMDiskCommand(&argPtr, booterParam); } else if (strcmp(booterCommand, "norescan") == 0) { if (gEnableCDROMRescan) { gEnableCDROMRescan = false; break; } } else { showHelp(); } key = 0; showBootPrompt(nextRow, showPrompt); break; } gBootVolume = menuBVR; setRootVolume(menuBVR); gBIOSDev = menuBVR->biosdev; break; case kEscapeKey: clearBootArgs(); break; case kF2Key: /* * AutoResolution - Reapply the patch if Graphics Mode was incorrect or EDID Info was insane */ if ((gAutoResolution == TRUE) && map) { // get the new Graphics Mode key processBootOptions(); UInt32 params[4]; params[3] = 0; //Has the target Resolution Changed ? int count = getNumberArrayFromProperty(kGraphicsModeKey, params, 4); if ( count < 3 ) getResolution(params); if ((params[0] != 0) && (params[1] != 0) && (params[0] != map->currentX) && (params[1] != map->currentY)) { //Go back to TEXT mode while we change the mode if (bootArgs->Video.v_display == GRAPHICS_MODE) { CursorState cursorState; setVideoMode(VGA_TEXT_MODE, 0); setCursorPosition(0, 0, 0); clearScreenRows(0, kScreenLastRow); changeCursor( 0, 0, kCursorTypeHidden, &cursorState ); //Reapply patch in case resolution have changed patchVbios(map, params[0], params[1], params[2], 0, 0); if (useGUI && (gui.initialised == true)) initGUI(); // Make sure all values are set if (bootArgs->Video.v_display != GRAPHICS_MODE) bootArgs->Video.v_display = GRAPHICS_MODE; if (!useGUI) useGUI = true; // redraw the background buffer drawBackground(); gui.devicelist.draw = true; gui.redraw = true; if (!(gBootMode & kBootModeQuiet)) { bool showBootBanner = true; // Check config file. getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->bootConfig); if (showBootBanner) // Display banner and show hardware info. gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024); // redraw background memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4); } nextRow = kMenuTopRow; showPrompt = true; if (gDeviceCount) { showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); nextRow += min( gDeviceCount, kMenuMaxItems ) + 3; } // Show the boot prompt. showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt( nextRow, showPrompt ); //this is used to avoid resetting the incorrect mode while quiting the boot menu map->hasSwitched = true; } } clearBootArgs(); key = 0; } break; case kF5Key: // New behavior: // Clear gBootVolume to restart the loop // if the user enabled rescanning the optical drive. // Otherwise boot the default boot volume. if (gEnableCDROMRescan) { gBootVolume = NULL; clearBootArgs(); } break; case kF10Key: gScanSingleDrive = false; scanDisks(gBIOSDev, &bvCount); gBootVolume = NULL; clearBootArgs(); break; case kTabKey: // New behavior: // Switch between text & graphic interfaces // Only Permitted if started in graphics interface if (useGUI) { setVideoMode(VGA_TEXT_MODE, 0); setCursorPosition(0, 0, 0); clearScreenRows(0, kScreenLastRow); // Display banner and show hardware info. printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024); printf(getVBEInfoString()); clearScreenRows(kMenuTopRow, kMenuTopRow + 2); changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0); nextRow = kMenuTopRow; showPrompt = true; if (gDeviceCount) { printf("Use \30\31 keys to select the startup volume."); showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems); nextRow += min(gDeviceCount, kMenuMaxItems) + 3; } showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt(nextRow, showPrompt); //changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); /* * AutoResolution - make sure all values are set */ bootArgs->Video.v_display = VGA_TEXT_MODE; useGUI = false; } else { gui.redraw = true; setVideoMode(GRAPHICS_MODE, 0); /* * AutoResolution - make sure all values are set */ bootArgs->Video.v_display = GRAPHICS_MODE; useGUI = true; updateVRAM(); } key = 0; break; default: key = 0; break; } } while (0 == key); done: if (bootArgs->Video.v_display == VGA_TEXT_MODE) { clearScreenRows(kMenuTopRow, kScreenLastRow); changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0); } shouldboot = false; gui.menu.draw = false; if (menuItems) { free(menuItems); menuItems = NULL; } return 0; }
//========================================================================== // The 'main' function for the booter. Called by boot0 when booting // from a block device, or by the network booter. // // arguments: // biosdev - Value passed from boot1/NBP to specify the device // that the booter was loaded from. // // If biosdev is kBIOSDevNetwork, then this function will return if // booting was unsuccessful. This allows the PXE firmware to try the // next boot device on its list. void common_boot(int biosdev) { int status; char *bootFile; unsigned long adler32; bool quiet; bool firstRun = true; bool instantMenu; bool rescanPrompt; unsigned int allowBVFlags = kBVFlagSystemVolume|kBVFlagForeignBoot; unsigned int denyBVFlags = kBVFlagEFISystem; // Set reminder to unload the PXE base code. Neglect to unload // the base code will result in a hang or kernel panic. gUnloadPXEOnExit = true; // Record the device that the booter was loaded from. gBIOSDev = biosdev & kBIOSDevMask; // Initialize boot info structure. initKernBootStruct(); // Setup VGA text mode. // Not sure if it is safe to call setVideoMode() before the // config table has been loaded. Call video_mode() instead. #if DEBUG printf("before video_mode\n"); #endif video_mode( 2 ); // 80x25 mono text mode. #if DEBUG printf("after video_mode\n"); #endif // Scan and record the system's hardware information. scan_platform(); // First get info for boot volume. scanBootVolumes(gBIOSDev, 0); bvChain = getBVChainForBIOSDev(gBIOSDev); setBootGlobals(bvChain); // Load boot.plist config file status = loadSystemConfig(&bootInfo->bootConfig); if (getBoolForKey(kQuietBootKey, &quiet, &bootInfo->bootConfig) && quiet) { gBootMode |= kBootModeQuiet; } // Override firstRun to get to the boot menu instantly by setting "Instant Menu"=y in system config if (getBoolForKey(kInsantMenuKey, &instantMenu, &bootInfo->bootConfig) && instantMenu) { firstRun = false; } // Loading preboot ramdisk if exists. loadPrebootRAMDisk(); // Disable rescan option by default gEnableCDROMRescan = false; // Enable it with Rescan=y in system config if (getBoolForKey(kRescanKey, &gEnableCDROMRescan, &bootInfo->bootConfig) && gEnableCDROMRescan) { gEnableCDROMRescan = true; } // Ask the user for Rescan option by setting "Rescan Prompt"=y in system config. rescanPrompt = false; if (getBoolForKey(kRescanPromptKey, &rescanPrompt , &bootInfo->bootConfig) && rescanPrompt && biosDevIsCDROM(gBIOSDev)) { gEnableCDROMRescan = promptForRescanOption(); } // Enable touching a single BIOS device only if "Scan Single Drive"=y is set in system config. if (getBoolForKey(kScanSingleDriveKey, &gScanSingleDrive, &bootInfo->bootConfig) && gScanSingleDrive) { gScanSingleDrive = true; } // Create a list of partitions on device(s). if (gScanSingleDrive) { scanBootVolumes(gBIOSDev, &bvCount); } else { scanDisks(gBIOSDev, &bvCount); } // Create a separated bvr chain using the specified filters. bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); gBootVolume = selectBootVolume(bvChain); #if DEBUG printf(" Default: %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBootVolume, gBootVolume->biosdev, gBootVolume->part_no, gBootVolume->flags); printf(" bt(0,0): %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBIOSBootVolume, gBIOSBootVolume->biosdev, gBIOSBootVolume->part_no, gBIOSBootVolume->flags); getc(); #endif useGUI = TRUE; // Override useGUI default getBoolForKey(kGUIKey, &useGUI, &bootInfo->bootConfig); // Before initGui, patch the video bios with the correct resolution UInt32 params[4]; int count; params[3] = 0; autoResolution = TRUE; // Override AutoResolution default getBoolForKey(kAutoResolutionKey, &autoResolution, &bootInfo->bootConfig); vbios_map * map = open_vbios(CT_UNKWN); //Saving the bios in case we have to unpatch it save_vbios(map); if (autoResolution == TRUE) { //Get Resolution from Graphics Mode key count = getNumberArrayFromProperty(kGraphicsModeKey, params, 4); if ( count < 3 ){ //If no Graphics Mode key, get from EDID getResolution(¶ms[0], ¶ms[1], ¶ms[2]); verbose("EDID Resolution: %dx%d\n",params[0], params[1]); } } else { if ( params[2] == 256 ) params[2] = 8; if ( params[2] == 555 ) params[2] = 16; if ( params[2] == 888 ) params[2] = 32; } if (params[0]!=0 && params[1]!=0) { unlock_vbios(map); patch_vbios(map, params[0], params[1], params[2], 0, 0); relock_vbios(map); #if AUTORES_DEBUG printf("Press Any Key...\n"); getc(); #endif } if (useGUI) { /* XXX AsereBLN handle error */ initGUI(); } setBootGlobals(bvChain); // Parse args, load and start kernel. while (1) { const char *val; int len; int trycache; long flags, cachetime, kerneltime, exttime, sleeptime, time; int ret = -1; void *binary = (void *)kLoadAddr; bool tryresume; bool tryresumedefault; bool forceresume; config_file_t systemVersion; // system.plist of booting partition // additional variable for testing alternate kernel image locations on boot helper partitions. char bootFileSpec[512]; // Initialize globals. sysConfigValid = false; gErrors = false; status = getBootOptions(firstRun); firstRun = false; if (status == -1) continue; status = processBootOptions(); // Status==1 means to chainboot if ( status == 1 ) break; // Status==-1 means that the config file couldn't be loaded or that gBootVolume is NULL if ( status == -1 ) { // gBootVolume == NULL usually means the user hit escape. if(gBootVolume == NULL) { freeFilteredBVChain(bvChain); if (gEnableCDROMRescan) rescanBIOSDevice(gBIOSDev); bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); setBootGlobals(bvChain); } continue; } // Other status (e.g. 0) means that we should proceed with boot. if( bootArgs->Video.v_display == GRAPHICS_MODE ) drawBackground(); // Found and loaded a config file. Proceed with boot. // Turn off any GUI elements if( bootArgs->Video.v_display == GRAPHICS_MODE ) { gui.devicelist.draw = false; gui.bootprompt.draw = false; gui.menu.draw = false; gui.infobox.draw = false; drawBackground(); updateVRAM(); } /* * AutoResolution - Reapply the patch or cancel if Graphics Mode was incorrect * or EDID Info was insane */ getBoolForKey(kAutoResolutionKey, &autoResolution, &bootInfo->bootConfig); //Restore the vbios for Cancelation if ((autoResolution == FALSE) && map) { unlock_vbios(map); restore_vbios(map); relock_vbios(map); close_vbios(map); } if ((autoResolution == TRUE) && map) { //Reapply patch in case resolution have changed count = getNumberArrayFromProperty(kGraphicsModeKey, params, 4); if ( count < 3 ) { getResolution(¶ms[0], ¶ms[1], ¶ms[2]); } else { if ( params[2] == 256 ) params[2] = 8; if ( params[2] == 555 ) params[2] = 16; if ( params[2] == 888 ) params[2] = 32; } if (params[0]!=0 && params[1]!=0) { unlock_vbios(map); patch_vbios(map, params[0], params[1], params[2], 0, 0); relock_vbios(map); close_vbios(map); } } // Find out which version mac os we're booting. if (!loadConfigFile("System/Library/CoreServices/SystemVersion.plist", &systemVersion)) { if (getValueForKey(kProductVersion, &val, &len, &systemVersion)) { // getValueForKey uses const char for val // so copy it and trim strncpy(gMacOSVersion, val, MIN(len, 4)); gMacOSVersion[MIN(len, 4)] = '\0'; } } if (platformCPUFeature(CPU_FEATURE_EM64T)) { archCpuType = CPU_TYPE_X86_64; } else { archCpuType = CPU_TYPE_I386; } if (getValueForKey(karch, &val, &len, &bootInfo->bootConfig)) { if (strncmp(val, "i386", 4) == 0) { archCpuType = CPU_TYPE_I386; } } if (getValueForKey(k32BitModeFlag, &val, &len, &bootInfo->bootConfig)) { archCpuType = CPU_TYPE_I386; } if (!getBoolForKey (kWake, &tryresume, &bootInfo->bootConfig)) { tryresume = true; tryresumedefault = true; } else { tryresumedefault = false; } if (!getBoolForKey (kForceWake, &forceresume, &bootInfo->bootConfig)) { forceresume = false; } if (forceresume) { tryresume = true; tryresumedefault = false; } while (tryresume) { const char *tmp; BVRef bvr; if (!getValueForKey(kWakeImage, &val, &len, &bootInfo->bootConfig)) val="/private/var/vm/sleepimage"; // Do this first to be sure that root volume is mounted ret = GetFileInfo(0, val, &flags, &sleeptime); if ((bvr = getBootVolumeRef(val, &tmp)) == NULL) break; // Can't check if it was hibernation Wake=y is required if (bvr->modTime == 0 && tryresumedefault) break; if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) break; if (!forceresume && ((sleeptime+3)<bvr->modTime)) { printf ("Hibernate image is too old by %d seconds. Use ForceWake=y to override\n",bvr->modTime-sleeptime); break; } HibernateBoot((char *)val); break; } // Reset cache name. bzero(gCacheNameAdler + 64, sizeof(gCacheNameAdler) - 64); sprintf(gCacheNameAdler + 64, "%s,%s", gRootDevice, bootInfo->bootFile); adler32 = Adler32((unsigned char *)gCacheNameAdler, sizeof(gCacheNameAdler)); if (getValueForKey(kKernelCacheKey, &val, &len, &bootInfo->bootConfig)) { strlcpy(gBootKernelCacheFile, val, len+1); } else { sprintf(gBootKernelCacheFile, "%s.%08lX", kDefaultCachePath, adler32); } // Check for cache file. trycache = (((gBootMode & kBootModeSafe) == 0) && !gOverrideKernel && (gBootFileType == kBlockDeviceType) && (gMKextName[0] == '\0') && (gBootKernelCacheFile[0] != '\0')); verbose("Loading Darwin %s\n", gMacOSVersion); if (trycache) do { // if we haven't found the kernel yet, don't use the cache ret = GetFileInfo(NULL, bootInfo->bootFile, &flags, &kerneltime); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) { trycache = 0; break; } ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat) || (cachetime < kerneltime)) { trycache = 0; break; } ret = GetFileInfo("/System/Library/", "Extensions", &flags, &exttime); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory) && (cachetime < exttime)) { trycache = 0; break; } if (kerneltime > exttime) { exttime = kerneltime; } if (cachetime != (exttime + 1)) { trycache = 0; break; } } while (0); do { if (trycache) { bootFile = gBootKernelCacheFile; verbose("Loading kernel cache %s\n", bootFile); ret = LoadFile(bootFile); binary = (void *)kLoadAddr; if (ret >= 0) { break; } } bootFile = bootInfo->bootFile; // Try to load kernel image from alternate locations on boot helper partitions. sprintf(bootFileSpec, "com.apple.boot.P/%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { sprintf(bootFileSpec, "com.apple.boot.R/%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { sprintf(bootFileSpec, "com.apple.boot.S/%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { // Not found any alternate locations, using the original kernel image path. strcpy(bootFileSpec, bootFile); } } } verbose("Loading kernel %s\n", bootFileSpec); ret = LoadThinFatFile(bootFileSpec, &binary); if (ret <= 0 && archCpuType == CPU_TYPE_X86_64) { archCpuType = CPU_TYPE_I386; ret = LoadThinFatFile(bootFileSpec, &binary); } } while (0); clearActivityIndicator(); #if DEBUG printf("Pausing..."); sleep(8); #endif if (ret <= 0) { printf("Can't find %s\n", bootFile); if(gui.initialised) { sleep(1); drawBackground(); gui.devicelist.draw = true; gui.redraw = true; } if (gBootFileType == kNetworkDeviceType) { // Return control back to PXE. Don't unload PXE base code. gUnloadPXEOnExit = false; break; } } else { /* Won't return if successful. */ ret = ExecKernel(binary); } } // chainboot if (status==1) { if (getVideoMode() == GRAPHICS_MODE) { // if we are already in graphics-mode, setVideoMode(VGA_TEXT_MODE, 0); // switch back to text mode } } if ((gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit) { nbpUnloadBaseCode(); } }
int getBootOptions(bool firstRun) { int i; int key; int nextRow; int timeout; int bvCount; BVRef bvr; BVRef menuBVR; bool showPrompt, newShowPrompt, isCDROM; // Initialize default menu selection entry. gBootVolume = menuBVR = selectBootVolume(bvChain); if (biosDevIsCDROM(gBIOSDev)) { isCDROM = true; } else { isCDROM = false; } // ensure we're in graphics mode if gui is setup if (firstRun && gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE) { setVideoMode(GRAPHICS_MODE, 0); } // Clear command line boot arguments clearBootArgs(); // Allow user to override default timeout. if (multiboot_timeout_set) { timeout = multiboot_timeout; } else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->chameleonConfig)) { /* If there is no timeout key in the file use the default timeout which is different for CDs vs. hard disks. However, if not booting a CD and no config file could be loaded set the timeout to zero which causes the menu to display immediately. This way, if no partitions can be found, that is the disk is unpartitioned or simply cannot be read) then an empty menu is displayed. If some partitions are found, for example a Windows partition, then these will be displayed in the menu as foreign partitions. */ if (isCDROM) { timeout = kCDBootTimeout; } else { timeout = sysConfigValid ? kBootTimeout : 0; } } if (timeout < 0) { gBootMode |= kBootModeQuiet; } // If the user is holding down a modifier key, enter safe mode. if ((readKeyboardShiftFlags() & 0x0F) != 0) { gBootMode |= kBootModeSafe; } // Checking user pressed keys bool f8press = false, spress = false, vpress = false; while (readKeyboardStatus()) { key = bgetc (); if (key == 0x4200) f8press = true; if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true; if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true; } // If user typed F8, abort quiet mode, and display the menu. if (f8press) { gBootMode &= ~kBootModeQuiet; timeout = 0; } // If user typed 'v' or 'V', boot in verbose mode. if ((gBootMode & kBootModeQuiet) && firstRun && vpress) { addBootArg(kVerboseModeFlag); } // If user typed 's' or 'S', boot in single user mode. if ((gBootMode & kBootModeQuiet) && firstRun && spress) { addBootArg(kSingleUserModeFlag); } if (bootArgs->Video.v_display == VGA_TEXT_MODE) { setCursorPosition(0, 0, 0); clearScreenRows(0, kScreenLastRow); if (!(gBootMode & kBootModeQuiet)) { // Display banner and show hardware info. printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024); printf(getVBEInfoString()); } changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0); verbose("Scanning device %x...", gBIOSDev); } // When booting from CD, default to hard drive boot when possible. if (isCDROM && firstRun) { const char *val; char *prompt = NULL; char *name = NULL; int cnt; int optionKey; if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->chameleonConfig)) { prompt = malloc(cnt + 1); strncat(prompt, val, cnt); } else { name = malloc(80); getBootVolumeDescription(gBootVolume, name, 79, false); prompt = malloc(256); sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name); free(name); } if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->chameleonConfig )) { // The key specified is a special key. } else { // Default to F8. optionKey = 0x4200; } // If the timeout is zero then it must have been set above due to the // early catch of F8 which means the user wants to set boot options // which we ought to interpret as meaning he wants to boot the CD. if (timeout != 0) { key = countdown(prompt, kMenuTopRow, timeout); } else { key = optionKey; } if (prompt != NULL) { free(prompt); } clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); // Hit the option key ? if (key == optionKey) { gBootMode &= ~kBootModeQuiet; timeout = 0; } else { key = key & 0xFF; // Try booting hard disk if user pressed 'h' if (biosDevIsCDROM(gBIOSDev) && key == 'h') { BVRef bvr; // Look at partitions hosting OS X other than the CD-ROM for (bvr = bvChain; bvr; bvr=bvr->next) { if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) { gBootVolume = bvr; } } } goto done; } } if (gBootMode & kBootModeQuiet) { // No input allowed from user. goto done; } if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) { // If the user is holding down a modifier key, // enter safe mode. if ((readKeyboardShiftFlags() & 0x0F) != 0) { gBootMode |= kBootModeSafe; } goto done; } if (gDeviceCount > 0) { // Allocate memory for an array of menu items. menuItems = malloc(sizeof(MenuItem) * gDeviceCount); if (menuItems == NULL) { goto done; } // Associate a menu item for each BVRef. for (bvr=bvChain, i=gDeviceCount-1, selectIndex=-1; bvr; bvr=bvr->next) { if (bvr->visible) { getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true); menuItems[i].param = (void *) bvr; if (bvr == menuBVR) { selectIndex = i; } i--; } } // Jief : In case the default partition (returned by selectBootVolume) is not in the menu if ( selectIndex == -1 ) { selectIndex = 0; // gDeviceCount is actually > 0, so menuItems[selectIndex] exists menuBVR = (BVRef)(menuItems[selectIndex].param); // what happen is bvChain is empty ? } } if (bootArgs->Video.v_display != VGA_TEXT_MODE) { // redraw the background buffer gui.logo.draw = true; drawBackground(); gui.devicelist.draw = true; gui.redraw = true; if (!(gBootMode & kBootModeQuiet)) { // Check if "Boot Banner"=N switch is present in config file. getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig); if (showBootBanner) { // Display banner and show hardware info. gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024); } // redraw background memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4); } } else { // Clear screen and hide the blinking cursor. clearScreenRows(kMenuTopRow, kMenuTopRow + 2); changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0); } nextRow = kMenuTopRow; showPrompt = true; if (gDeviceCount) { if( bootArgs->Video.v_display == VGA_TEXT_MODE ) { printf("Use \30\31 keys to select the startup volume."); } showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3; } // Show the boot prompt. showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt( nextRow, showPrompt ); do { if (bootArgs->Video.v_display != VGA_TEXT_MODE) { // redraw background memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 ); // reset cursor co-ords gui.debug.cursor = pos( gui.screen.width - 160 , 10 ); } key = getchar(); updateMenu( key, (void **) &menuBVR ); newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); if (newShowPrompt != showPrompt) { showPrompt = newShowPrompt; showBootPrompt( nextRow, showPrompt ); } if (showPrompt) { updateBootArgs(key); } switch (key) { case KEY_ENTER: if (gui.menu.draw) { key=0; break; } if (*gBootArgs == '?') { char * argPtr = gBootArgs; // Skip the leading "?" character. argPtr++; getNextArg(&argPtr, booterCommand); getNextArg(&argPtr, booterParam); /* * TODO: this needs to be refactored. */ if (strcmp( booterCommand, "video" ) == 0) { if (bootArgs->Video.v_display != VGA_TEXT_MODE) { showInfoBox(getVBEInfoString(), getVBEModeInfoString()); } else { printVBEModeInfo(); } } else if ( strcmp( booterCommand, "memory" ) == 0) { if (bootArgs->Video.v_display != VGA_TEXT_MODE ) { showInfoBox("Memory Map", getMemoryInfoString()); } else { printMemoryInfo(); } } else if (strcmp(booterCommand, "lspci") == 0) { lspci(); } else if (strcmp(booterCommand, "log") == 0) { showTextBuffer(msgbuf, strlen(msgbuf)); } else if (strcmp(booterCommand, "more") == 0) { showTextFile(booterParam); } else if (strcmp(booterCommand, "rd") == 0) { processRAMDiskCommand(&argPtr, booterParam); } else if (strcmp(booterCommand, "norescan") == 0) { if (gEnableCDROMRescan) { gEnableCDROMRescan = false; break; } } else { showHelp(); } key = 0; showBootPrompt(nextRow, showPrompt); break; } gBootVolume = menuBVR; setRootVolume(menuBVR); gBIOSDev = menuBVR->biosdev; break; case KEY_ESC: clearBootArgs(); break; case KEY_F5: // New behavior: // Clear gBootVolume to restart the loop // if the user enabled rescanning the optical drive. // Otherwise boot the default boot volume. if (gEnableCDROMRescan) { gBootVolume = NULL; clearBootArgs(); } break; case KEY_F10: gScanSingleDrive = false; scanDisks(gBIOSDev, &bvCount); gBootVolume = NULL; clearBootArgs(); break; case KEY_TAB: // New behavior: // Switch between text & graphic interfaces // Only Permitted if started in graphics interface if (useGUI) { if (bootArgs->Video.v_display != VGA_TEXT_MODE) { setVideoMode(VGA_TEXT_MODE, 0); setCursorPosition(0, 0, 0); clearScreenRows(0, kScreenLastRow); // Display banner and show hardware info. printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024); printf(getVBEInfoString()); clearScreenRows(kMenuTopRow, kMenuTopRow + 2); changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0); nextRow = kMenuTopRow; showPrompt = true; if (gDeviceCount) { printf("Use \30\31 keys to select the startup volume."); showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems); nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3; } showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt(nextRow, showPrompt); //changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); } else { gui.redraw = true; setVideoMode(GRAPHICS_MODE, 0); updateVRAM(); updateGraphicBootPrompt(); } } key = 0; break; default: key = 0; break; } } while (0 == key); done: if (bootArgs->Video.v_display == VGA_TEXT_MODE) { clearScreenRows(kMenuTopRow, kScreenLastRow); changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0); } shouldboot = false; gui.menu.draw = false; if (menuItems) { free(menuItems); menuItems = NULL; } execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL); return 0; }
//========================================================================== // The 'main' function for the booter. Called by boot0 when booting // from a block device, or by the network booter. // // arguments: // biosdev - Value passed from boot1/NBP to specify the device // that the booter was loaded from. // // If biosdev is kBIOSDevNetwork, then this function will return if // booting was unsuccessful. This allows the PXE firmware to try the // next boot device on its list. void common_boot(int biosdev) { bool quiet; bool firstRun = true; bool instantMenu; bool rescanPrompt; int status; unsigned int allowBVFlags = kBVFlagSystemVolume | kBVFlagForeignBoot; unsigned int denyBVFlags = kBVFlagEFISystem; // Set reminder to unload the PXE base code. Neglect to unload // the base code will result in a hang or kernel panic. gUnloadPXEOnExit = true; // Record the device that the booter was loaded from. gBIOSDev = biosdev & kBIOSDevMask; // Initialize boot-log initBooterLog(); // Initialize boot info structure. initKernBootStruct(); // Setup VGA text mode. // Not sure if it is safe to call setVideoMode() before the // config table has been loaded. Call video_mode() instead. #if DEBUG printf("before video_mode\n"); #endif video_mode( 2 ); // 80x25 mono text mode. #if DEBUG printf("after video_mode\n"); #endif // Scan and record the system's hardware information. scan_platform(); // First get info for boot volume. scanBootVolumes(gBIOSDev, 0); bvChain = getBVChainForBIOSDev(gBIOSDev); setBootGlobals(bvChain); // Load boot.plist config file status = loadChameleonConfig(&bootInfo->chameleonConfig, bvChain); if (getBoolForKey(kQuietBootKey, &quiet, &bootInfo->chameleonConfig) && quiet) { gBootMode |= kBootModeQuiet; } // Override firstRun to get to the boot menu instantly by setting "Instant Menu"=y in system config if (getBoolForKey(kInstantMenuKey, &instantMenu, &bootInfo->chameleonConfig) && instantMenu) { firstRun = false; } // Loading preboot ramdisk if exists. loadPrebootRAMDisk(); // Disable rescan option by default gEnableCDROMRescan = false; // Enable it with Rescan=y in system config if (getBoolForKey(kRescanKey, &gEnableCDROMRescan, &bootInfo->chameleonConfig) && gEnableCDROMRescan) { gEnableCDROMRescan = true; } // Ask the user for Rescan option by setting "Rescan Prompt"=y in system config. rescanPrompt = false; if (getBoolForKey(kRescanPromptKey, &rescanPrompt , &bootInfo->chameleonConfig) && rescanPrompt && biosDevIsCDROM(gBIOSDev)) { gEnableCDROMRescan = promptForRescanOption(); } // Enable touching a single BIOS device only if "Scan Single Drive"=y is set in system config. if (getBoolForKey(kScanSingleDriveKey, &gScanSingleDrive, &bootInfo->chameleonConfig) && gScanSingleDrive) { gScanSingleDrive = true; } // Create a list of partitions on device(s). if (gScanSingleDrive) { scanBootVolumes(gBIOSDev, &bvCount); } else { scanDisks(gBIOSDev, &bvCount); } // Create a separated bvr chain using the specified filters. bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); gBootVolume = selectBootVolume(bvChain); // Intialize module system init_module_system(); #if DEBUG printf(" Default: %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBootVolume, gBootVolume->biosdev, gBootVolume->part_no, gBootVolume->flags); printf(" bt(0,0): %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBIOSBootVolume, gBIOSBootVolume->biosdev, gBIOSBootVolume->part_no, gBIOSBootVolume->flags); getchar(); #endif useGUI = true; // Override useGUI default getBoolForKey(kGUIKey, &useGUI, &bootInfo->chameleonConfig); if (useGUI && initGUI()) { // initGUI() returned with an error, disabling GUI. useGUI = false; } setBootGlobals(bvChain); // Parse args, load and start kernel. while (1) { bool tryresume, tryresumedefault, forceresume; bool useKernelCache = true; // by default try to use the prelinked kernel const char *val; int len, ret = -1; long flags, sleeptime, time; void *binary = (void *)kLoadAddr; char bootFile[sizeof(bootInfo->bootFile)]; char bootFilePath[512]; char kernelCacheFile[512]; // Initialize globals. sysConfigValid = false; gErrors = false; status = getBootOptions(firstRun); firstRun = false; if (status == -1) continue; status = processBootOptions(); // Status == 1 means to chainboot if ( status == 1 ) break; // Status == -1 means that the config file couldn't be loaded or that gBootVolume is NULL if ( status == -1 ) { // gBootVolume == NULL usually means the user hit escape. if (gBootVolume == NULL) { freeFilteredBVChain(bvChain); if (gEnableCDROMRescan) rescanBIOSDevice(gBIOSDev); bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); setBootGlobals(bvChain); setupDeviceList(&bootInfo->themeConfig); } continue; } // Other status (e.g. 0) means that we should proceed with boot. // Turn off any GUI elements if ( bootArgs->Video.v_display == GRAPHICS_MODE ) { gui.devicelist.draw = false; gui.bootprompt.draw = false; gui.menu.draw = false; gui.infobox.draw = false; gui.logo.draw = false; drawBackground(); updateVRAM(); } // Find out which version mac os we're booting. getOSVersion(); if (platformCPUFeature(CPU_FEATURE_EM64T)) { archCpuType = CPU_TYPE_X86_64; } else { archCpuType = CPU_TYPE_I386; } if (getValueForKey(karch, &val, &len, &bootInfo->chameleonConfig)) { if (strncmp(val, "i386", 4) == 0) { archCpuType = CPU_TYPE_I386; } } if (getValueForKey(kKernelArchKey, &val, &len, &bootInfo->chameleonConfig)) { if (strncmp(val, "i386", 4) == 0) { archCpuType = CPU_TYPE_I386; } } // Notify modules that we are attempting to boot execute_hook("PreBoot", NULL, NULL, NULL, NULL); if (!getBoolForKey (kWake, &tryresume, &bootInfo->chameleonConfig)) { tryresume = true; tryresumedefault = true; } else { tryresumedefault = false; } if (!getBoolForKey (kForceWake, &forceresume, &bootInfo->chameleonConfig)) { forceresume = false; } if (forceresume) { tryresume = true; tryresumedefault = false; } while (tryresume) { const char *tmp; BVRef bvr; if (!getValueForKey(kWakeImage, &val, &len, &bootInfo->chameleonConfig)) val = "/private/var/vm/sleepimage"; // Do this first to be sure that root volume is mounted ret = GetFileInfo(0, val, &flags, &sleeptime); if ((bvr = getBootVolumeRef(val, &tmp)) == NULL) break; // Can't check if it was hibernation Wake=y is required if (bvr->modTime == 0 && tryresumedefault) break; if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) break; if (!forceresume && ((sleeptime+3)<bvr->modTime)) { #if DEBUG printf ("Hibernate image is too old by %d seconds. Use ForceWake=y to override\n", bvr->modTime-sleeptime); #endif break; } HibernateBoot((char *)val); break; } getBoolForKey(kUseKernelCache, &useKernelCache, &bootInfo->chameleonConfig); if (useKernelCache) do { // Determine the name of the Kernel Cache if (getValueForKey(kKernelCacheKey, &val, &len, &bootInfo->bootConfig)) { if (val[0] == '\\') { len--; val++; } /* FIXME: check len vs sizeof(kernelCacheFile) */ strlcpy(kernelCacheFile, val, len + 1); } else { kernelCacheFile[0] = 0; // Use default kernel cache file } if (gOverrideKernel && kernelCacheFile[0] == 0) { DBG("Using a non default kernel (%s) without specifying 'Kernel Cache' path, KernelCache will not be used\n", bootInfo->bootFile); useKernelCache = false; break; } if (gMKextName[0] != 0) { DBG("Using a specific MKext Cache (%s), KernelCache will not be used\n", gMKextName); useKernelCache = false; break; } if (gBootFileType != kBlockDeviceType) useKernelCache = false; } while(0); do { if (useKernelCache) { ret = LoadKernelCache(kernelCacheFile, &binary); if (ret >= 0) break; } bool bootFileWithDevice = false; // Check if bootFile start with a device ex: bt(0,0)/Extra/mach_kernel if (strncmp(bootInfo->bootFile,"bt(",3) == 0 || strncmp(bootInfo->bootFile,"hd(",3) == 0 || strncmp(bootInfo->bootFile,"rd(",3) == 0) bootFileWithDevice = true; // bootFile must start with a / if it not start with a device name if (!bootFileWithDevice && (bootInfo->bootFile)[0] != '/') { snprintf(bootFile, sizeof(bootFile), "/%s", bootInfo->bootFile); // append a leading / } else { strlcpy(bootFile, bootInfo->bootFile, sizeof(bootFile)); } // Try to load kernel image from alternate locations on boot helper partitions. ret = -1; if ((gBootVolume->flags & kBVFlagBooter) && !bootFileWithDevice) { snprintf(bootFilePath, sizeof(bootFilePath), "com.apple.boot.P%s", bootFile); ret = GetFileInfo(NULL, bootFilePath, &flags, &time); if (ret == -1) { snprintf(bootFilePath, sizeof(bootFilePath), "com.apple.boot.R%s", bootFile); ret = GetFileInfo(NULL, bootFilePath, &flags, &time); if (ret == -1) { snprintf(bootFilePath, sizeof(bootFilePath), "com.apple.boot.S%s", bootFile); ret = GetFileInfo(NULL, bootFilePath, &flags, &time); } } } if (ret == -1) { // No alternate location found, using the original kernel image path. strlcpy(bootFilePath, bootFile, sizeof(bootFilePath)); } DBG("Loading kernel: '%s'\n", bootFilePath); ret = LoadThinFatFile(bootFilePath, &binary); if (ret <= 0 && archCpuType == CPU_TYPE_X86_64) { archCpuType = CPU_TYPE_I386; ret = LoadThinFatFile(bootFilePath, &binary); } } while (0); clearActivityIndicator(); #if DEBUG printf("Pausing..."); sleep(8); #endif if (ret <= 0) { printf("Can't find %s\n", bootFile); sleep(1); if (gBootFileType == kNetworkDeviceType) { // Return control back to PXE. Don't unload PXE base code. gUnloadPXEOnExit = false; break; } pause(); } else { /* Won't return if successful. */ ret = ExecKernel(binary); } } // chainboot if (status == 1) { // if we are already in graphics-mode, if (getVideoMode() == GRAPHICS_MODE) { setVideoMode(VGA_TEXT_MODE, 0); // switch back to text mode. } } if ((gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit) { nbpUnloadBaseCode(); } }
int getBootOptions(bool firstRun) { int i; int key; int nextRow; int timeout; #if UNUSED int bvCount; #endif BVRef bvr; BVRef menuBVR; bool showPrompt, newShowPrompt, isCDROM; int optionKey; // Initialize default menu selection entry. menuBVR = selectBootVolume(getBvChain()); safe_set_env(envgBootVolume, (uint32_t)menuBVR); if (biosDevIsCDROM((int)get_env(envgBIOSDev))) { isCDROM = true; } else { isCDROM = false; } // Clear command line boot arguments clearBootArgs(); // Allow user to override default timeout. #if UNUSED if (multiboot_timeout_set) { timeout = multiboot_timeout; } else #endif if (!getIntForKey(kTimeoutKey, &timeout, DEFAULT_BOOT_CONFIG)) { /* If there is no timeout key in the file use the default timeout which is different for CDs vs. hard disks. However, if not booting a CD and no config file could be loaded set the timeout to zero which causes the menu to display immediately. This way, if no partitions can be found, that is the disk is unpartitioned or simply cannot be read) then an empty menu is displayed. If some partitions are found, for example a Windows partition, then these will be displayed in the menu as foreign partitions. */ if (isCDROM) { timeout = kCDBootTimeout; } else { timeout = get_env(envSysConfigValid) ? kBootTimeout : 0; } } long gBootMode = (long)get_env(envgBootMode); if (timeout < 0) { gBootMode |= kBootModeQuiet; safe_set_env(envgBootMode,gBootMode); } // If the user is holding down a modifier key, enter safe mode. if ((readKeyboardShiftFlags() & 0x0F) != 0) { gBootMode |= kBootModeSafe; safe_set_env(envgBootMode,gBootMode); } // Checking user pressed keys bool f8press = false, spress = false, vpress = false; while (readKeyboardStatus()) { key = bgetc (); if (key == 0x4200) f8press = true; if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true; if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true; } // If user typed F8, abort quiet mode, and display the menu. if (f8press) { gBootMode &= ~kBootModeQuiet; safe_set_env(envgBootMode,gBootMode); timeout = 0; } // If user typed 'v' or 'V', boot in verbose mode. if ((gBootMode & kBootModeQuiet) && firstRun && vpress) { addBootArg(kVerboseModeFlag); } // If user typed 's' or 'S', boot in single user mode. if ((gBootMode & kBootModeQuiet) && firstRun && spress) { addBootArg(kSingleUserModeFlag); } setCursorPosition(0, 0, 0); clearScreenRows(0, kScreenLastRow); if (!(gBootMode & kBootModeQuiet)) { // Display banner and show hardware info. char * bootBanner = (char*)(uint32_t)get_env(envBootBanner); printf(bootBanner, (int)(get_env(envConvMem) + get_env(envExtMem)) / 1024); } changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0); msglog("Scanning device %x...", (uint32_t)get_env(envgBIOSDev)); // When booting from CD, default to hard drive boot when possible. if (isCDROM && firstRun) { const char *val; char *prompt = NULL; char *name = NULL; int cnt; if (getValueForKey(kCDROMPromptKey, &val, &cnt, DEFAULT_BOOT_CONFIG)) { prompt = calloc(cnt + 1, sizeof(char)); if (!prompt) { stop("Couldn't allocate memory for the prompt\n"); //TODO: Find a better stategie return -1; } strncat(prompt, val, cnt); } else { prompt = calloc(256, sizeof(char)); if (!prompt) { stop("Couldn't allocate memory for the prompt\n"); //TODO: Find a better stategie return -1; } BVRef bvr; if (( bvr = ((BVRef)(uint32_t)get_env(envgBootVolume)))) { name = calloc(80, sizeof(char)); if (!name) { stop("Couldn't allocate memory for the device name\n"); //TODO: Find a better stategie return -1; } getBootVolumeDescription(bvr, name, 79, false); snprintf(prompt, 256,"Press ENTER to start up from %s, or press any key to enter startup options.", name); free(name); } else snprintf(prompt, 256,"Press ENTER to start up, or press any key to enter startup options."); } if (getIntForKey( kCDROMOptionKey, &optionKey, DEFAULT_BOOT_CONFIG )) { // The key specified is a special key. } else { // Default to F8. optionKey = 0x4200; } // If the timeout is zero then it must have been set above due to the // early catch of F8 which means the user wants to set boot options // which we ought to interpret as meaning he wants to boot the CD. if (timeout != 0) { key = countdown(prompt, kMenuTopRow, timeout, &optionKey); } else { key = optionKey; } if (prompt != NULL) { free(prompt); } clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); do { // Hit the option key ? if (key == optionKey) { if (key != 0x1C0D) { gBootMode &= ~kBootModeQuiet; safe_set_env(envgBootMode,gBootMode); timeout = 0; break; } } key = key & 0xFF; // Try booting hard disk if user pressed 'h' if (biosDevIsCDROM((int)get_env(envgBIOSDev)) && key == 'h') { BVRef bvr; // Look at partitions hosting OS X other than the CD-ROM for (bvr = getBvChain(); bvr; bvr=bvr->next) { if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != (int)get_env(envgBIOSDev)) { safe_set_env(envgBootVolume, (uint32_t)bvr); } } } goto done; } while (0); } if (get_env(envgBootMode) & kBootModeQuiet) { // No input allowed from user. goto done; } if (firstRun && timeout > 0 ) { key = countdown("Press ENTER to start up, or press any key to enter startup options.", kMenuTopRow, timeout, &optionKey); if (key == 0x1C0D) { goto done; } else if (key == 0) { // If the user is holding down a modifier key, // enter safe mode. if ((readKeyboardShiftFlags() & 0x0F) != 0) { gBootMode |= kBootModeSafe; safe_set_env(envgBootMode,gBootMode); } goto done; } } int devcnt = (int)get_env(envgDeviceCount); if (devcnt) { // Allocate memory for an array of menu items. menuItems = calloc(devcnt,sizeof(MenuItem)); if (menuItems == NULL) { goto done; } // Associate a menu item for each BVRef. for (bvr=getBvChain(), i=devcnt-1, selectIndex=0; bvr; bvr=bvr->next) { if (bvr->visible) { getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true); menuItems[i].param = (void *) bvr; if (bvr == menuBVR) { selectIndex = i; } i--; } } } // Clear screen and hide the blinking cursor. clearScreenRows(kMenuTopRow, kMenuTopRow + 2); changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0); nextRow = kMenuTopRow; /*showPrompt = true;*/ if (devcnt) { printf("Use \30\31 keys to select the startup volume."); showMenu( menuItems, devcnt, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); nextRow += min( devcnt, kMenuMaxItems ) + 3; } // Show the boot prompt. showPrompt = (devcnt == 0) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt( nextRow, showPrompt ); do { key = getc(); updateMenu( key, (void **) &menuBVR ); newShowPrompt = (devcnt == 0) || (menuBVR->flags & kBVFlagNativeBoot); if (newShowPrompt != showPrompt) { showPrompt = newShowPrompt; showBootPrompt( nextRow, showPrompt ); } if (showPrompt) { updateBootArgs(key); } switch (key) { case kReturnKey: if (*gBootArgs == '?') { char * argPtr = gBootArgs; // Skip the leading "?" character. argPtr++; getNextArg(&argPtr, booterCommand); getNextArg(&argPtr, booterParam); /* * TODO: this needs to be refactored. */ #if UNUSED if (strncmp( booterCommand, "video", sizeof("video") ) == 0) { printVBEModeInfo(); } else #endif if ( strncmp( booterCommand, "memory", sizeof("memory") ) == 0) { printMemoryInfo(); } else if (strncmp(booterCommand, "lspci", sizeof( "lspci")) == 0) { lspci(); } else if (strncmp(booterCommand, "more", sizeof("more")) == 0) { showTextFile(booterParam); } else if (strncmp(booterCommand, "rd", sizeof("rd")) == 0) { if (execute_hook("processRAMDiskCommand", (void*)argPtr, &booterParam, NULL, NULL, NULL, NULL) != EFI_SUCCESS) showMessage("ramdisk module not found, please install RamdiskLoader.dylib in /Extra/modules/"); } else if (strncmp(booterCommand, "norescan", sizeof("norescan")) == 0) { if (get_env(envgEnableCDROMRescan)) { safe_set_env(envgEnableCDROMRescan,false); break; } } else { showHelp(); } key = 0; showBootPrompt(nextRow, showPrompt); break; } safe_set_env(envgBootVolume, (uint32_t)menuBVR); setRootVolume(menuBVR); safe_set_env(envgBIOSDev,menuBVR->biosdev); break; case kEscapeKey: clearBootArgs(); break; case kF5Key: // New behavior: // Clear gBootVolume to restart the loop // if the user enabled rescanning the optical drive. // Otherwise boot the default boot volume. if (get_env(envgEnableCDROMRescan)) { //gBootVolume = NULL; safe_set_env(envgBootVolume, (uint32_t)NULL); clearBootArgs(); } break; case kF10Key: safe_set_env(envgScanSingleDrive, false); scanDisks(); //gBootVolume = NULL; safe_set_env(envgBootVolume, (uint32_t)NULL); clearBootArgs(); break; default: key = 0; break; } } while (0 == key); done: if (bootArgs->Video.v_display == VGA_TEXT_MODE) { clearScreenRows(kMenuTopRow, kScreenLastRow); changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0); } safe_set_env(envShouldboot, false); if (menuItems) { free(menuItems); menuItems = NULL; } return 0; }
//========================================================================== // The 'main' function for the booter. Called by boot0 when booting // from a block device, or by the network booter. // // arguments: // biosdev - Value passed from boot1/NBP to specify the device // that the booter was loaded from. // // If biosdev is kBIOSDevNetwork, then this function will return if // booting was unsuccessful. This allows the PXE firmware to try the // next boot device on its list. void common_boot(int biosdev) { bool quiet; bool firstRun = true; bool instantMenu; bool rescanPrompt; char *bootFile; int status; unsigned int allowBVFlags = kBVFlagSystemVolume | kBVFlagForeignBoot; unsigned int denyBVFlags = kBVFlagEFISystem; unsigned long adler32; // Set reminder to unload the PXE base code. Neglect to unload // the base code will result in a hang or kernel panic. gUnloadPXEOnExit = true; // Record the device that the booter was loaded from. gBIOSDev = biosdev & kBIOSDevMask; // Initialize boot info structure. initKernBootStruct(); initBooterLog(); // Setup VGA text mode. // Not sure if it is safe to call setVideoMode() before the // config table has been loaded. Call video_mode() instead. #if DEBUG printf("before video_mode\n"); #endif video_mode( 2 ); // 80x25 mono text mode. #if DEBUG printf("after video_mode\n"); #endif // Scan and record the system's hardware information. scan_platform(); // First get info for boot volume. scanBootVolumes(gBIOSDev, 0); bvChain = getBVChainForBIOSDev(gBIOSDev); setBootGlobals(bvChain); // Load boot.plist config file status = loadChameleonConfig(&bootInfo->chameleonConfig); if (getBoolForKey(kQuietBootKey, &quiet, &bootInfo->chameleonConfig) && quiet) { gBootMode |= kBootModeQuiet; } // Override firstRun to get to the boot menu instantly by setting "Instant Menu"=y in system config if (getBoolForKey(kInstantMenuKey, &instantMenu, &bootInfo->chameleonConfig) && instantMenu) { firstRun = false; } // Loading preboot ramdisk if exists. loadPrebootRAMDisk(); // Disable rescan option by default gEnableCDROMRescan = false; // Enable it with Rescan=y in system config if (getBoolForKey(kRescanKey, &gEnableCDROMRescan, &bootInfo->chameleonConfig) && gEnableCDROMRescan) { gEnableCDROMRescan = true; } // Ask the user for Rescan option by setting "Rescan Prompt"=y in system config. rescanPrompt = false; if (getBoolForKey(kRescanPromptKey, &rescanPrompt , &bootInfo->chameleonConfig) && rescanPrompt && biosDevIsCDROM(gBIOSDev)) { gEnableCDROMRescan = promptForRescanOption(); } // Enable touching a single BIOS device only if "Scan Single Drive"=y is set in system config. if (getBoolForKey(kScanSingleDriveKey, &gScanSingleDrive, &bootInfo->chameleonConfig) && gScanSingleDrive) { gScanSingleDrive = true; } // Create a list of partitions on device(s). if (gScanSingleDrive) { scanBootVolumes(gBIOSDev, &bvCount); } else { scanDisks(gBIOSDev, &bvCount); } // Create a separated bvr chain using the specified filters. bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); gBootVolume = selectBootVolume(bvChain); // Intialize module system init_module_system(); #if DEBUG printf(" Default: %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBootVolume, gBootVolume->biosdev, gBootVolume->part_no, gBootVolume->flags); printf(" bt(0,0): %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBIOSBootVolume, gBIOSBootVolume->biosdev, gBIOSBootVolume->part_no, gBIOSBootVolume->flags); getchar(); #endif useGUI = true; // Override useGUI default getBoolForKey(kGUIKey, &useGUI, &bootInfo->chameleonConfig); if (useGUI && initGUI()) { // initGUI() returned with an error, disabling GUI. useGUI = false; } setBootGlobals(bvChain); // Parse args, load and start kernel. while (1) { bool tryresume, tryresumedefault, forceresume; bool usecache = false;//true; const char *val; int len, trycache, ret = -1; long flags, cachetime, kerneltime, exttime, sleeptime, time; void *binary = (void *)kLoadAddr; // additional variable for testing alternate kernel image locations on boot helper partitions. char bootFileSpec[512]; // Initialize globals. sysConfigValid = false; gErrors = false; status = getBootOptions(firstRun); firstRun = false; if (status == -1) continue; status = processBootOptions(); // Status == 1 means to chainboot if ( status == 1 ) break; // Status == -1 means that the config file couldn't be loaded or that gBootVolume is NULL if ( status == -1 ) { // gBootVolume == NULL usually means the user hit escape. if (gBootVolume == NULL) { freeFilteredBVChain(bvChain); if (gEnableCDROMRescan) rescanBIOSDevice(gBIOSDev); bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); setBootGlobals(bvChain); setupDeviceList(&bootInfo->themeConfig); } continue; } // Other status (e.g. 0) means that we should proceed with boot. // Turn off any GUI elements if ( bootArgs->Video.v_display == GRAPHICS_MODE ) { gui.devicelist.draw = false; gui.bootprompt.draw = false; gui.menu.draw = false; gui.infobox.draw = false; gui.logo.draw = false; drawBackground(); updateVRAM(); } // Find out which version mac os we're booting. getOSVersion(); if (platformCPUFeature(CPU_FEATURE_EM64T)) { archCpuType = CPU_TYPE_X86_64; } else { archCpuType = CPU_TYPE_I386; } if (getValueForKey(karch, &val, &len, &bootInfo->chameleonConfig)) { if (strncmp(val, "i386", 4) == 0) { archCpuType = CPU_TYPE_I386; } } if (getValueForKey(kKernelArchKey, &val, &len, &bootInfo->chameleonConfig)) { if (strncmp(val, "i386", 4) == 0) { archCpuType = CPU_TYPE_I386; } } // Notify modules that we are attempting to boot execute_hook("PreBoot", NULL, NULL, NULL, NULL); if (!getBoolForKey (kWake, &tryresume, &bootInfo->chameleonConfig)) { tryresume = true; tryresumedefault = true; } else { tryresumedefault = false; } if (!getBoolForKey (kForceWake, &forceresume, &bootInfo->chameleonConfig)) { forceresume = false; } if (forceresume) { tryresume = true; tryresumedefault = false; } while (tryresume) { const char *tmp; BVRef bvr; if (!getValueForKey(kWakeImage, &val, &len, &bootInfo->chameleonConfig)) val = "/private/var/vm/sleepimage"; // Do this first to be sure that root volume is mounted ret = GetFileInfo(0, val, &flags, &sleeptime); if ((bvr = getBootVolumeRef(val, &tmp)) == NULL) break; // Can't check if it was hibernation Wake=y is required if (bvr->modTime == 0 && tryresumedefault) break; if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) break; if (!forceresume && ((sleeptime+3)<bvr->modTime)) { #if DEBUG printf ("Hibernate image is too old by %d seconds. Use ForceWake=y to override\n", bvr->modTime-sleeptime); #endif break; } HibernateBoot((char *)val); break; } getBoolForKey(kUseKernelCache, &usecache, &bootInfo->chameleonConfig); if (usecache) { if( getValueForKey(kKernelCacheKey, &val, &len, &bootInfo->chameleonConfig) || getValueForKey(kKernelCacheKey, &val, &len, &bootInfo->bootConfig) ) { if (val[0] == '\\') { len--; val++; } strlcpy(gBootKernelCacheFile, val, len + 1); verbose("Using kernel cache: \"%s\" \n", gBootKernelCacheFile); //sleep(5); } else { // Lion and Mountain Lion prelink kernel cache file␊ if ((checkOSVersion("10.7")) || (checkOSVersion("10.8"))) { sprintf(gBootKernelCacheFile, "%skernelcache", kDefaultCachePathSnow); } // Snow Leopard else if (checkOSVersion("10.6")) { sprintf(gBootKernelCacheFile, "kernelcache_%s", (archCpuType == CPU_TYPE_I386) ? "i386" : "x86_64"); int lnam = sizeof(gBootKernelCacheFile) + 9; //with adler32 char* name; long prev_time = 0; struct dirstuff* cacheDir = opendir(kDefaultCachePathSnow); while(readdir(cacheDir, (const char**)&name, &flags, &time) >= 0) { if (((flags & kFileTypeMask) != kFileTypeDirectory) && time > prev_time && strstr(name, gBootKernelCacheFile) && (name[lnam] != '.')) { sprintf(gBootKernelCacheFile, "%s%s", kDefaultCachePathSnow, name); prev_time = time; } } } else { // Reset cache name. bzero(gCacheNameAdler + 64, sizeof(gCacheNameAdler) - 64); sprintf(gCacheNameAdler + 64, "%s,%s", gRootDevice, bootInfo->bootFile); adler32 = Adler32((unsigned char *)gCacheNameAdler, sizeof(gCacheNameAdler)); sprintf(gBootKernelCacheFile, "%s.%08lX", kDefaultCachePathLeo, adler32); } } } // Check for cache file. trycache = (usecache && ((gBootMode & kBootModeSafe) == 0) && !gOverrideKernel && (gBootFileType == kBlockDeviceType) && (gMKextName[0] == '\0') && (gBootKernelCacheFile[0] != '\0')); verbose("trycache: %d\n", trycache); verbose("Loading Darwin %s\n", gMacOSVersion); if (trycache) do { verbose("bootInfo->bootFile: \"%s\" \n", bootInfo->bootFile); ret = GetFileInfo(NULL, bootInfo->bootFile, &flags, &kerneltime); if (ret != 0) kerneltime = 0; else if ((flags & kFileTypeMask) != kFileTypeFlat) { trycache = 0; verbose("trycache0 : 1\n"); break; } ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime); if ((ret != 0)) { trycache = 0; verbose("trycache0 : 2.1 \"%s\" \n", gBootKernelCacheFile); break; } else if ( ((flags & kFileTypeMask) != kFileTypeFlat) ) { trycache = 0; verbose("trycache0 : 2.2\n"); break; } else if ( (cachetime < kerneltime) ) { trycache = 0; verbose("trycache0 : 2.3\n"); break; } ret = GetFileInfo("/System/Library/", "Extensions", &flags, &exttime); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory) && (cachetime < exttime)) { trycache = 0; verbose("trycache0 : 3\n"); break; } if (ret == 0 && kerneltime > exttime) { exttime = kerneltime; } if (ret == 0 && cachetime < (exttime + 1)) { trycache = 0; verbose("trycache0 : 4\n"); break; } } while (0); // sleep(5); do { if (trycache) { bootFile = gBootKernelCacheFile; verbose("Loading kernel cache %s\n", bootFile); if ((checkOSVersion("10.7")) || (checkOSVersion("10.8"))) { ret = LoadThinFatFile(bootFile, &binary); } else { ret = LoadFile(bootFile); binary = (void *)kLoadAddr; } if (ret >= 0) break; verbose("Kernel cache did not load %s\n ", bootFile); } if ((checkOSVersion("10.7")) || (checkOSVersion("10.8"))) { bootFile = gBootKernelCacheFile; } else { sprintf(bootFile, "\%s", bootInfo->bootFile); } // Try to load kernel image from alternate locations on boot helper partitions. sprintf(bootFileSpec, "com.apple.boot.P%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { sprintf(bootFileSpec, "com.apple.boot.R%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { sprintf(bootFileSpec, "com.apple.boot.S%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { // No alternate location found, using the original kernel image path. strcpy(bootFileSpec, bootInfo->bootFile); } } } if ((checkOSVersion("10.7")) || (checkOSVersion("10.8"))) { //Lion, dont load kernel if haz cache if (!trycache) { verbose("Loading kernel %s\n", bootFileSpec); ret = LoadThinFatFile(bootFileSpec, &binary); if (ret <= 0 && archCpuType == CPU_TYPE_X86_64) { archCpuType = CPU_TYPE_I386; ret = LoadThinFatFile(bootFileSpec, &binary); } } else ret = 1; } else { //Snow Leopard or older verbose("Loading kernel %s\n", bootFileSpec); ret = LoadThinFatFile(bootFileSpec, &binary); if (ret <= 0 && archCpuType == CPU_TYPE_X86_64) { archCpuType = CPU_TYPE_I386; ret = LoadThinFatFile(bootFileSpec, &binary); } } } while (0); clearActivityIndicator(); #if DEBUG printf("Pausing..."); sleep(8); #endif if (ret <= 0) { printf("Can't find %s\n", bootFile); sleep(1); if (gBootFileType == kNetworkDeviceType) { // Return control back to PXE. Don't unload PXE base code. gUnloadPXEOnExit = false; break; } } else { /* Won't return if successful. */ ret = ExecKernel(binary); } } // chainboot if (status == 1) { // if we are already in graphics-mode, if (getVideoMode() == GRAPHICS_MODE) { setVideoMode(VGA_TEXT_MODE, 0); // switch back to text mode. } } if ((gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit) { nbpUnloadBaseCode(); } }
//========================================================================== // The 'main' function for the booter. Called by boot0 when booting // from a block device, or by the network booter. // // arguments: // biosdev - Value passed from boot1/NBP to specify the device // that the booter was loaded from. // // If biosdev is kBIOSDevNetwork, then this function will return if // booting was unsuccessful. This allows the PXE firmware to try the // next boot device on its list. void common_boot(int biosdev) { int status; char *bootFile; unsigned long adler32; bool quiet; bool firstRun = true; bool instantMenu; bool rescanPrompt = false; unsigned int allowBVFlags = kBVFlagSystemVolume|kBVFlagForeignBoot; unsigned int denyBVFlags = kBVFlagEFISystem; // Set reminder to unload the PXE base code. Neglect to unload // the base code will result in a hang or kernel panic. gUnloadPXEOnExit = true; // Record the device that the booter was loaded from. gBIOSDev = biosdev & kBIOSDevMask; // Initialize boot info structure. initKernBootStruct(); initBooterLog(); // Setup VGA text mode. // Not sure if it is safe to call setVideoMode() before the // config table has been loaded. Call video_mode() instead. #if DEBUG printf("before video_mode\n"); //Azi: this one is not printing... i remember it did.. check trunk. #endif video_mode( 2 ); // 80x25 mono text mode. #if DEBUG printf("after video_mode\n"); #endif // Scan and record the system's hardware information. scan_platform(); // First get info for boot volume. scanBootVolumes(gBIOSDev, 0); bvChain = getBVChainForBIOSDev(gBIOSDev); //Azi: initialising gBIOSBootVolume & gBootVolume (Startup volume) for the first time.. i think!? // also, kDefaultPartitionKey is checked here, on selectBootVolume. setBootGlobals(bvChain); msglog("setBootGlobals:\n Default: %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBootVolume, gBootVolume->biosdev, gBootVolume->part_no, gBootVolume->flags); msglog(" bt(0,0): %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBIOSBootVolume, gBIOSBootVolume->biosdev, gBIOSBootVolume->part_no, gBIOSBootVolume->flags); // Boot Volume is set as Root at this point so, pointing to Extra, /Extra or bt(0,0)/Extra // is exactly the same. Review bt(0,0)/bla bla paths...... (Reviewing...) //Azi: works as expected but... trying this because Kernel=mach_kernel doesn't work on a // override Boot.plist; this makes it impossible to override e.g. Kernel=bt(0,0)mach_kernel // on the main Boot.plist, when loading kernel from ramdisk btAliased. loadPrebootRAMDisk(); // Load boot.plist config file //Azi: on this first check, boot.plist acts as both "booter config file" // and bootargs/options "carrier".***** status = loadSystemConfig(&bootInfo->bootConfig); if (getBoolForKey(kQuietBootKey, &quiet, &bootInfo->bootConfig) && quiet) { gBootMode |= kBootModeQuiet; } // Override firstRun to get to the boot menu instantly by setting "Instant Menu"=y in system config if (getBoolForKey(kInstantMenuKey, &instantMenu, &bootInfo->bootConfig) && instantMenu) { firstRun = false; } // Loading preboot ramdisk if exists. // loadPrebootRAMDisk(); //Azi: this needs to be done before load_all_modules() // because of btAlias... (Reviewing...) // Intialize module system if (init_module_system()) { load_all_modules(); } // Disable rescan option by default gEnableCDROMRescan = false; // If we're loading the booter from optical media... (Reviewing...) if (biosDevIsCDROM(gBIOSDev)) { // ... ask the user for Rescan option by setting "Rescan Prompt"=y in system config... if (getBoolForKey(kRescanPromptKey, &rescanPrompt, &bootInfo->bootConfig) && rescanPrompt) { gEnableCDROMRescan = promptForRescanOption(); } else // ... or enable it with Rescan=y in system config. if (getBoolForKey(kRescanKey, &gEnableCDROMRescan, &bootInfo->bootConfig) && gEnableCDROMRescan) { gEnableCDROMRescan = true; } } //Azi: Is this a cdrom only thing? (Reviewing...) // Enable touching a single BIOS device only if "Scan Single Drive"=y is set in system config. if (getBoolForKey(kScanSingleDriveKey, &gScanSingleDrive, &bootInfo->bootConfig) && gScanSingleDrive) { scanBootVolumes(gBIOSDev, &bvCount); } else { //Azi: scanDisks uses scanBootVolumes. scanDisks(gBIOSDev, &bvCount); } // Create a separated bvr chain using the specified filters. bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); gBootVolume = selectBootVolume(bvChain); //#if DEBUG //printf msglog(":something...???\n Default: %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBootVolume, gBootVolume->biosdev, gBootVolume->part_no, gBootVolume->flags); msglog(" bt(0,0): %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBIOSBootVolume, gBIOSBootVolume->biosdev, gBIOSBootVolume->part_no, gBIOSBootVolume->flags); // getchar(); //#endif useGUI = true; // Override useGUI default getBoolForKey(kGUIKey, &useGUI, &bootInfo->bootConfig); // AutoResolution - Azi: default to false // http://forum.voodooprojects.org/index.php/topic,1227.0.html gAutoResolution = false; // Check if user enabled AutoResolution on Boot.plist... getBoolForKey(kAutoResolutionKey, &gAutoResolution, &bootInfo->bootConfig); // Patch the Video Bios with the extracted resolution, before initGui. if (gAutoResolution == true) { initAutoRes(); } if (useGUI && initGUI()) { // initGUI() returned with an error, disabling GUI. useGUI = false; } setBootGlobals(bvChain); msglog("setBootGlobals:\n Default: %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBootVolume, gBootVolume->biosdev, gBootVolume->part_no, gBootVolume->flags); msglog(" bt(0,0): %d, ->biosdev: %d, ->part_no: %d ->flags: %d\n", gBIOSBootVolume, gBIOSBootVolume->biosdev, gBIOSBootVolume->part_no, gBIOSBootVolume->flags); // Parse args, load and start kernel. while (1) { const char *val; int len; int trycache; long flags, cachetime, kerneltime, exttime, sleeptime, time; int ret = -1; void *binary = (void *)kLoadAddr; bool tryresume; bool tryresumedefault; bool forceresume; bool ignoreKC = false; // additional variable for testing alternate kernel image locations on boot helper partitions. char bootFileSpec[512]; // Initialize globals. sysConfigValid = false; gErrors = false; status = getBootOptions(firstRun); firstRun = false; if (status == -1) continue; //Azi: test (gBootVolume == NULL) - so far Ok! // test with optical media again...? // Turn off any GUI elements, draw background and update VRAM. if ( bootArgs->Video.v_display == GRAPHICS_MODE ) { gui.devicelist.draw = false; gui.bootprompt.draw = false; gui.menu.draw = false; gui.infobox.draw = false; gui.logo.draw = false; drawBackground(); updateVRAM(); } status = processBootOptions(); //Azi: AutoResolution - closing Vbios here without restoring, causes an allocation error, // if the user tries to boot, after a e.g."Can't find bla_kernel" msg. // Doing it on execKernel() instead. // Status == 1 means to chainboot if ( status == 1 ) break; // Status == -1 means that gBootVolume is NULL. Config file is not mandatory anymore! if ( status == -1 ) { // gBootVolume == NULL usually means the user hit escape. (Reviewing...) if (gBootVolume == NULL) { freeFilteredBVChain(bvChain); if (gEnableCDROMRescan) rescanBIOSDevice(gBIOSDev); bvChain = newFilteredBVChain(0x80, 0xFF, allowBVFlags, denyBVFlags, &gDeviceCount); setBootGlobals(bvChain); setupDeviceList(&bootInfo->themeConfig); } continue; } // Other status (e.g. 0) means that we should proceed with boot. // If cpu handles 64 bit instructions... if (platformCPUFeature(CPU_FEATURE_EM64T)) { // use x86_64 kernel arch,... archCpuType = CPU_TYPE_X86_64; } else { // else use i386 kernel arch. archCpuType = CPU_TYPE_I386; } // If user override... if (getValueForKey(kArchKey, &val, &len, &bootInfo->bootConfig)) { // matches i386... if (strncmp(val, "i386", 4) == 0) { // use i386 kernel arch. archCpuType = CPU_TYPE_I386; } } if (!getBoolForKey (kWakeKey, &tryresume, &bootInfo->bootConfig)) { tryresume = true; tryresumedefault = true; } else { tryresumedefault = false; } if (!getBoolForKey (kForceWakeKey, &forceresume, &bootInfo->bootConfig)) { forceresume = false; } if (forceresume) { tryresume = true; tryresumedefault = false; } while (tryresume) { const char *tmp; BVRef bvr; if (!getValueForKey(kWakeKeyImageKey, &val, &len, &bootInfo->bootConfig)) val="/private/var/vm/sleepimage"; // Do this first to be sure that root volume is mounted ret = GetFileInfo(0, val, &flags, &sleeptime); if ((bvr = getBootVolumeRef(val, &tmp)) == NULL) break; // Can't check if it was hibernation Wake=y is required if (bvr->modTime == 0 && tryresumedefault) break; if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) break; if (!forceresume && ((sleeptime+3)<bvr->modTime)) { //Azi: no need for printf at this point - reminder printf ("Hibernate image is too old by %d seconds. Use ForceWake=y to override.\n",bvr->modTime-sleeptime); break; } HibernateBoot((char *)val); break; } //Azi:kernelcache stuff bool patchKernel = false; getBoolForKey(kKPatcherKey, &patchKernel, &bootInfo->bootConfig); //Azi: avoiding having to use -f to ignore kernel cache //Azi: ignore kernel cache but still use kext cache (E/E.mkext & S/L/E.mkext). - explain... getBoolForKey(kUseKCKey, &ignoreKC, &bootInfo->bootConfig); // equivalent to UseKernelCache if (ignoreKC) { verbose("KC: cache ignored by user.\n"); // make sure the damn thing get's zeroed, just in case... :)* bzero(gBootKernelCacheFile, sizeof(gBootKernelCacheFile)); } else if (patchKernel) // to be moved..? { verbose("KC: kernel patcher enabled, ignore cache.\n"); bzero(gBootKernelCacheFile, sizeof(gBootKernelCacheFile)); } else if (getValueForKey(kKernelCacheKey, &val, &len, &bootInfo->bootConfig)) { strlcpy(gBootKernelCacheFile, val, len + 1); verbose("KC: path set by user = %s\n", gBootKernelCacheFile); //Azi: bypass time check when user sets path ??? // cache is still ignored if time doesn't match... (e.g. booter on usb stick) } else { // Reset cache name. bzero(gCacheNameAdler + 64, sizeof(gCacheNameAdler) - 64); // kextcache_main.c: Construct entry from UUID of boot volume...(reminder) // assemble ?string? to generate adler from... // sprintf(gCacheNameAdler + 64, "%s,%s", gRootDevice, bootInfo->bootFile); - OLD const char *ProductName = getStringForKey("SMproductname", &bootInfo->smbiosConfig); sprintf(gCacheNameAdler, ProductName); // well, at least the smbios.plist can be loaded this early... // to set/get "ProductName" this early, booter needs complete rewrite!! // see DHP's Revolution booter rework example! // verbose("KC: gCacheNameAdler 1 = %s\n", gCacheNameAdler); //Azi: check the validity of this, e.g. on Helper Partitions sprintf(gCacheNameAdler + 64, "%s", "\\System\\Library\\CoreServices\\boot.efi"); // verbose("KC: gCacheNameAdler 2 = %s\n", gCacheNameAdler + 64); sprintf(gCacheNameAdler + (64 + 38), "%s", bootInfo->bootFile); // verbose("KC: gCacheNameAdler 3 = %s\n", gCacheNameAdler + (64 + 38)); // generate adler adler32 = Adler32((unsigned char *)gCacheNameAdler, sizeof(gCacheNameAdler)); // verbose("KC: Adler32 = %08X\n", adler32); //Azi: no check for OS version here ?? - yes there is :) // append arch and/or adler (checksum) to kc path... if (gMacOSVersion[3] <= '5') { sprintf(gBootKernelCacheFile, "%s.%08lX", kCachePathTigerLeopard, adler32); // verbose("KC: adler added to path = %s\n", gBootKernelCacheFile); } else { sprintf(gBootKernelCacheFile, "%s_%s.%08X", kCachePathSnowLion, (archCpuType == CPU_TYPE_I386) ? "i386" : "x86_64", adler32); // verbose("KC: arch & adler added to path = %s\n", gBootKernelCacheFile); } } // Check for cache file. //Azi: trycache is done if... trycache = ( ( (gBootMode & kBootModeSafe) == 0) //... we're not booting in safe mode (-x arg), && !gOverrideKernel // we're not overriding default kernel "name", && (gBootFileType == kBlockDeviceType) // we're booting from local storage device, && (gMKextName[0] == '\0') // "MKext Cache" key IS NOT in use, and && (gBootKernelCacheFile[0] != '\0') ); // gBootKernelCacheFile is populated. // we could add the use of "kernelpatcher" to this bunch..?? // verbose("Loading Darwin %s\n", gMacOSVersion); //Azi: move?? to getOSVersion? :) if (trycache) do { verbose("KC: checking kernel cache (system prelinked kernel)...\n"); // if we haven't found the kernel yet, don't use the cache ret = GetFileInfo(NULL, bootInfo->bootFile, &flags, &kerneltime); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) { verbose("KC: no kernel found (shouldn't happen?!?)\n"); trycache = 0; // ignore kernel cache... break; } verbose("KC: kerneltime = %d\n", kerneltime); ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime); if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat) || (cachetime < kerneltime)) { if (cachetime <= 100) // confirm: 100 = inexistent path, -xxxxxxxxx = wrong name // not confirming... i also get -xxxxxxxxx with inexisting prelinked kernel verbose("KC: cachetime = %d, kernel cache path/adler is incorrect, ignoring it. ??? \n", cachetime); else verbose("KC: cachetime = %d, kernel cache is older than the kernel, ignoring it.\n", cachetime); trycache = 0; break; } verbose("KC: cachetime = %d\n", cachetime); ret = GetFileInfo("/System/Library/", "Extensions", &flags, &exttime); if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory) && (cachetime < exttime)) { verbose("KC: exttime = %d, kernel cache is older than S/L/E, ignoring it.\n", exttime); trycache = 0; break; } verbose("KC: exttime = %d\n", exttime); if (kerneltime > exttime) // if S/L/E is older than the kernel... { verbose("KC: S/L/E is older than the kernel, matching exttime with kerneltime...\n"); exttime = kerneltime; } verbose("KC: exttime +1 = %d\n", exttime + 1); if (cachetime != (exttime + 1)) { verbose("KC: kernel cache time is diff from S/L/E time, ignoring it.\n"); trycache = 0; break; } verbose("KC: kernel cache found and up to date, will be used.\n"); } while (0); do { // Load kernel cache if not ignored. if (trycache) { bootFile = gBootKernelCacheFile; verbose("Loading kernel cache %s\n", bootFile); ret = LoadFile(bootFile); binary = (void *)kLoadAddr; if (ret >= 0) { break; } } bootFile = bootInfo->bootFile; // Try to load kernel image from alternate locations on boot helper partitions. sprintf(bootFileSpec, "com.apple.boot.P/%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { sprintf(bootFileSpec, "com.apple.boot.R/%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { sprintf(bootFileSpec, "com.apple.boot.S/%s", bootFile); ret = GetFileInfo(NULL, bootFileSpec, &flags, &time); if (ret == -1) { // No alternate location found, using the original kernel image path. strcpy(bootFileSpec, bootFile); } } } verbose("Loading kernel %s\n", bootFileSpec); ret = LoadThinFatFile(bootFileSpec, &binary); if (ret <= 0 && archCpuType == CPU_TYPE_X86_64) { archCpuType = CPU_TYPE_I386; ret = LoadThinFatFile(bootFileSpec, &binary); } } while (0); clearActivityIndicator(); /*#if DEBUG printf("Pausing..."); sleep(8); #endif Azi: annoying stuff :P */ if (ret <= 0) { printf("Can't find %s\n", bootFile); sleep(1); if (gBootFileType == kNetworkDeviceType) { // Return control back to PXE. Don't unload PXE base code. gUnloadPXEOnExit = false; break; } } else { // Won't return if successful. ret = ExecKernel(binary); } } // while (1) // chainboot if (status==1) { if (getVideoMode() == GRAPHICS_MODE) { // if we are already in graphics-mode, setVideoMode(VGA_TEXT_MODE, 0); // switch back to text mode } } if ((gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit) { nbpUnloadBaseCode(); } }