Exemple #1
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();
	}
}
Exemple #2
0
int
processBootOptions()
{
	const char *cp  = gBootArgs;
	const char *val = 0;
	const char *kernel;
	int         cnt;
	int         userCnt;
	int         cntRemaining;
	char       *argP;
	char       *configKernelFlags;
	char       *valueBuffer;

	valueBuffer = malloc(VALUE_SIZE);
    
	skipblanks( &cp );

	// Update the unit and partition number.

	if ( gBootVolume ) {
		if (!( gBootVolume->flags & kBVFlagNativeBoot )) {
			readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, (void *) 0x7c00 );
			//
			// Setup edx, and signal intention to chain load the
			// foreign booter.
			//

			chainbootdev  = gBootVolume->biosdev;
			chainbootflag = 1;

			return 1;
		}

		setRootVolume(gBootVolume);

	}
	// If no boot volume fail immediately because we're just going to fail
	// trying to load the config file anyway.
	else {
		return -1;
	}

	// Find out which version mac os we're booting.
	strncpy(gMacOSVersion, gBootVolume->OSVersion, sizeof(gMacOSVersion));

	// Load config table specified by the user, or use the default.

	if (!getValueForBootKey(cp, "config", &val, &cnt)) {
		val = 0;
		cnt = 0;
	}

	// Load com.apple.Boot.plist from the selected volume
	// and use its contents to override default bootConfig.

	loadSystemConfig(&bootInfo->bootConfig);
	loadChameleonConfig(&bootInfo->chameleonConfig, NULL);

	// Use the kernel name specified by the user, or fetch the name
	// in the config table, or use the default if not specified.
	// Specifying a kernel name on the command line, or specifying
	// a non-default kernel name in the config file counts as
	// overriding the kernel, which causes the kernelcache not
	// to be used.

	gOverrideKernel = false;
	if (( kernel = extractKernelName((char **)&cp) ))
	{
		strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
	}
	else
	{
		if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) )
		{
			strlcpy( bootInfo->bootFile, val, cnt+1 );
		}
		else
		{
			if( YOSEMITE ) // is 10.10
			{

				strlcpy( bootInfo->bootFile, kOSXKernel, sizeof(bootInfo->bootFile) );
				//printf(HEADER "/System/Library/Kernels/%s\n", bootInfo->bootFile);
			}
			else
			{ // OSX is not 10.10

				strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
				//printf(HEADER "/%s\n", bootInfo->bootFile);
			}
		}
	}

	if (!YOSEMITE) // not 10.10 so 10.9 and previus
	{
		if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0)
		{
	        	//printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
			gOverrideKernel = true;
		}
	}
	else
	{ // OSX is 10.10
		if (strcmp( bootInfo->bootFile, kOSXKernel ) != 0)
		{
        		//printf(HEADER "org.chameleon.Boot.plist found path for custom '%s' found!\n", bootInfo->bootFile);
			gOverrideKernel = true;
		}
	}

	// Ermac : Inject "kext-dev-mode=1" if OS X 10.10 is detected
	if( YOSEMITE ) // is 10.10
	{
		addBootArg("kext-dev-mode=1");
	}

	cntRemaining = BOOT_STRING_LEN - 2;  // save 1 for NULL, 1 for space
	argP = bootArgs->CommandLine;

	// Get config kernel flags, if not ignored.
	if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
            !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig ))
	{
		val = "";
		cnt = 0;
	}
	configKernelFlags = malloc(cnt + 1);
	strlcpy(configKernelFlags, val, cnt + 1);

	// boot-uuid can be set either on the command-line or in the config file
	if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
                             &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString)))
	{
		//
		// Try an alternate method for getting the root UUID on boot helper partitions.
		//
		if (gBootVolume->flags & kBVFlagBooter)
		{
			// Load the configuration store in the boot helper partition
			if (loadHelperConfig(&bootInfo->helperConfig) == 0)
			{
				val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
				if (val != NULL)
				{
					strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
				}
			}
		}
/*
		// Try to get the volume uuid string
		if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
		{
			gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
		}
*/
		// If we have the volume uuid add it to the commandline arguments
		if (strlen(gBootUUIDString))
		{
			copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
		}
		// Try to get the volume uuid string
		if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
		{
			gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
			DBG("boot-uuid: %s\n", gBootUUIDString);
		}
	}

	if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
                             &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE))
	{
		cnt = 0;
		if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig))
		{
			valueBuffer[0] = '*';
			cnt++;
			strlcpy(valueBuffer + 1, val, cnt);
			val = valueBuffer;
		}
		else
		{ /*
			if (strlen(gBootUUIDString))
			{
				val = "*uuid";
				cnt = 5;
			}
			else
			{ */
				// Don't set "rd=.." if there is no boot device key
				// and no UUID.
				val = "";
				cnt = 0;
			/* } */
		}

		if (cnt > 0)
		{
			copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
		}
		strlcpy( gRootDevice, val, (cnt + 1));
	}

	/*
	 * Removed. We don't need this anymore.
	 *
	if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
							 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler)))
	{
		getPlatformName(gPlatformName);
		copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
	}
	*/

	if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
        !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt))
	{
		if (gBootMode & kBootModeSafe)
		{
			copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
		}
	}

	// Store the merged kernel flags and boot args.

	cnt = strlen(configKernelFlags);
	if (cnt)
	{
		if (cnt > cntRemaining)
		{
			error("Warning: boot arguments too long, truncating\n");
			cnt = cntRemaining;
		}
		strncpy(argP, configKernelFlags, cnt);
		argP[cnt++] = ' ';
		cntRemaining -= cnt;
	}
	userCnt = strlen(cp);
	if (userCnt > cntRemaining)
	{
		error("Warning: boot arguments too long, truncating\n");
		userCnt = cntRemaining;
	}
	strncpy(&argP[cnt], cp, userCnt);
	argP[cnt+userCnt] = '\0';

	if(!shouldboot)
	{
		gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
			getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
		
		gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
			kBootModeSafe : kBootModeNormal;

		if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) )
		{
			gBootMode = kBootModeSafe;
		}
	}

	if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
	{
		strlcpy(gMKextName, val, cnt + 1);
	}
	else
	{
		gMKextName[0]=0;
	}

	free(configKernelFlags);
	free(valueBuffer);

	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();
	}
}