Esempio n. 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)
{
    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(&params[0], &params[1], &params[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(&params[0], &params[1], &params[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();
    }
}
Esempio n. 2
0
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;
}