/* * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 1999-2004 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 2.0 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "boot.h" #include "bootstruct.h" #include "fdisk.h" enum { kReturnKey = 0x0d, kEscapeKey = 0x1b, kBackspaceKey = 0x08, kASCIIKeyMask = 0x7f }; enum { kMenuTopRow = 5, kMenuMaxItems = 6, kScreenLastRow = 24 }; static void showHelp(); //========================================================================== typedef struct { int x; int y; int type; } CursorState; static void changeCursor( int col, int row, int type, CursorState * cs ) { if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type ); setCursorType( type ); setCursorPosition( col, row, 0 ); } static void moveCursor( int col, int row ) { setCursorPosition( col, row, 0 ); } static void restoreCursor( const CursorState * cs ) { setCursorPosition( cs->x, cs->y, 0 ); setCursorType( cs->type ); } //========================================================================== /* Flush keyboard buffer; returns TRUE if any of the flushed * characters was F8. */ static BOOL flushKeyboardBuffer() { BOOL status = FALSE; while ( readKeyboardStatus() ) { if (bgetc() == 0x4200) status = TRUE; } return status; } //========================================================================== static int countdown( const char * msg, int row, int timeout ) { unsigned long time; int ch = 0; int col = strlen(msg) + 1; flushKeyboardBuffer(); moveCursor( 0, row ); printf("%s", msg); for ( time = time18(), timeout++; timeout > 0; ) { if ((ch = readKeyboardStatus()) != 0) break; // Count can be interrupted by holding down shift, // control or alt key if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) { ch = 1; break; } if ( time18() >= time ) { time += 18; timeout--; moveCursor( col, row ); printf("(%d) ", timeout); } } flushKeyboardBuffer(); return ch; } //========================================================================== static char gBootArgs[BOOT_STRING_LEN]; static char * gBootArgsPtr = gBootArgs; static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1; static void clearBootArgs() { gBootArgsPtr = gBootArgs; memset( gBootArgs, '\0', BOOT_STRING_LEN ); } //========================================================================== static void showBootPrompt( int row, BOOL visible ) { extern char bootPrompt[]; changeCursor( 0, row, kCursorTypeUnderline, 0 ); clearScreenRows( row, kScreenLastRow ); clearBootArgs(); if ( visible ) { printf( "%s", bootPrompt ); } else { printf("Press Enter to start up the foreign OS. "); } } //========================================================================== static void updateBootArgs( int key ) { key &= kASCIIKeyMask; switch ( key ) { case kBackspaceKey: if ( gBootArgsPtr > gBootArgs ) { int x, y, t; getCursorPositionAndType( &x, &y, &t ); if ( x == 0 && y ) { x = 80; y--; } if (x) x--; setCursorPosition( x, y, 0 ); putca(' ', 0x07, 1); *gBootArgsPtr-- = '\0'; } break; default: if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd) { putchar(key); // echo to screen *gBootArgsPtr++ = key; } break; } } //========================================================================== typedef struct { char name[80]; void * param; } MenuItem; static const MenuItem * gMenuItems = NULL; static int gMenuItemCount; static int gMenuRow; static int gMenuHeight; static int gMenuTop; static int gMenuBottom; static int gMenuSelection; static void printMenuItem( const MenuItem * item, int highlight ) { printf(" "); if ( highlight ) putca(' ', 0x70, strlen(item->name) + 4); else putca(' ', 0x07, 40); printf(" %40s\n", item->name); } //========================================================================== static void showMenu( const MenuItem * items, int count, int selection, int row, int height ) { int i; CursorState cursorState; if ( items == NULL || count == 0 ) return; // head and tail points to the start and the end of the list. // top and bottom points to the first and last visible items // in the menu window. gMenuItems = items; gMenuRow = row; gMenuHeight = height; gMenuItemCount = count; gMenuTop = 0; gMenuBottom = min( count, height ) - 1; gMenuSelection = selection; // If the selected item is not visible, shift the list down. if ( gMenuSelection > gMenuBottom ) { gMenuTop += ( gMenuSelection - gMenuBottom ); gMenuBottom = gMenuSelection; } // Draw the visible items. changeCursor( 0, row, kCursorTypeHidden, &cursorState ); for ( i = gMenuTop; i <= gMenuBottom; i++ ) { printMenuItem( &items[i], (i == gMenuSelection) ); } restoreCursor( &cursorState ); } //========================================================================== static int updateMenu( int key, void ** paramPtr ) { int moved = 0; union { struct { unsigned int selectionUp : 1, selectionDown : 1, scrollUp : 1, scrollDown : 1; } f; unsigned int w; } draw = {{0}}; if ( NULL == gMenuItems ) return 0; // Look at the scan code. switch ( key ) { case 0x4800: // Up Arrow if ( gMenuSelection != gMenuTop ) draw.f.selectionUp = 1; else if ( gMenuTop > 0 ) draw.f.scrollDown = 1; break; case 0x5000: // Down Arrow if ( gMenuSelection != gMenuBottom ) draw.f.selectionDown = 1; else if ( gMenuBottom < (gMenuItemCount - 1) ) draw.f.scrollUp = 1; break; } if ( draw.w ) { if ( draw.f.scrollUp ) { scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1); gMenuTop++; gMenuBottom++; draw.f.selectionDown = 1; } if ( draw.f.scrollDown ) { scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1); gMenuTop--; gMenuBottom--; draw.f.selectionUp = 1; } if ( draw.f.selectionUp || draw.f.selectionDown ) { CursorState cursorState; // Set cursor at current position, and clear inverse video. changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, kCursorTypeHidden, &cursorState ); printMenuItem( &gMenuItems[gMenuSelection], 0 ); if ( draw.f.selectionUp ) gMenuSelection--; else gMenuSelection++; moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop ); printMenuItem( &gMenuItems[gMenuSelection], 1 ); restoreCursor( &cursorState ); } *paramPtr = gMenuItems[gMenuSelection].param; moved = 1; } return moved; } //========================================================================== static void skipblanks( const char ** cpp ) { while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp); } //========================================================================== static const char * extractKernelName( char ** cpp ) { char * kn = *cpp; char * cp = *cpp; char c; // Convert char to lower case. c = *cp | 0x20; // Must start with a letter or a '/'. if ( (c < 'a' || c > 'z') && ( c != '/' ) ) return 0; // Keep consuming characters until we hit a separator. while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') ) cp++; // Only SPACE or TAB separator is accepted. // Reject everything else. if (*cp == '=') return 0; // Overwrite the separator, and move the pointer past // the kernel name. if (*cp != '\0') *cp++ = '\0'; *cpp = cp; return kn; } //========================================================================== static void printMemoryInfo(void) { int line; int i; MemoryRange *mp = bootInfo->memoryMap; // Activate and clear page 1 setActiveDisplayPage(1); clearScreenRows(0, 24); setCursorPosition( 0, 0, 1 ); printf("BIOS reported memory ranges:\n"); line = 1; for (i=0; imemoryMapCount; i++) { printf("Base 0x%08lx%08lx, ", (unsigned long)(mp->base >> 32), (unsigned long)(mp->base)); printf("length 0x%08lx%08lx, type %lu\n", (unsigned long)(mp->length >> 32), (unsigned long)(mp->length), mp->type); if (line++ > 20) { printf("(Press a key to continue...)"); getc(); line = 0; } mp++; } if (line > 0) { printf("(Press a key to continue...)"); getc(); } setActiveDisplayPage(0); } //========================================================================== extern void printFrequencyInfo(); #include "bootmap.h" bool gDisplayAllBootVolumesInMenu = false; #if 0 static MenuItem * buildMenuItemsForDevice(int biosdev, int *pBvCount, BVRef menuBVR, int *pSelectIndex) { BVRef bvChain; BVRef bvr; int bvCount; int i; MenuItem *menuItems; bvChain = scanBootVolumes( gBIOSDev, &bvCount ); if(bvCount == 0) return NULL; menuItems = (MenuItem *) malloc( sizeof(MenuItem) * bvCount ); if ( menuItems == NULL ) return NULL; for ( bvr = bvChain, i = bvCount - 1, *pSelectIndex = 0; bvr; bvr = bvr->next, i-- ) { getBootVolumeDescription( bvr, menuItems[i].name, 80, YES ); menuItems[i].param = (void *) bvr; if ( bvr == menuBVR ) *pSelectIndex = i; } if(pBvCount != NULL) *pBvCount = bvCount; return menuItems; } #endif static MenuItem * buildMenuItemsForAllDevices(int firstdev, int *pBvCount, BVRef menuBVR, int *pSelectIndex, bool doCompleteScan) { BootBVMapRef deviceChain; MenuItem *menuItems; BootBVMapBVIterator devI; BVRef bvr; int i; int bvCount; deviceChain = scanBootVolumesOnAllDevices(gBIOSDev, &bvCount, doCompleteScan); if(deviceChain == NULL) return NULL; if(bvCount == 0) return NULL; menuItems = (MenuItem *) malloc( sizeof(MenuItem) * bvCount ); if ( menuItems == NULL ) return NULL; BootBVMapBVIteratorInit(&devI, deviceChain); for ( bvr = BootBVMapBVIteratorGetNext(&devI), i = bvCount - 1, *pSelectIndex = 0; bvr; bvr = BootBVMapBVIteratorGetNext(&devI), i-- ) { getBootVolumeDescription( bvr, menuItems[i].name, 80, YES ); menuItems[i].param = (void *) bvr; if ( bvr == menuBVR ) *pSelectIndex = i; } freeBootBVMap(deviceChain); if(pBvCount != NULL) *pBvCount = bvCount; return menuItems; } int getBootOptions(BOOL firstRun) { int key; int selectIndex = 0; int bvCount; int nextRow; int timeout; BVRef bvChain; BVRef menuBVR; BOOL showPrompt, newShowPrompt, isCDROM; MenuItem * menuItems = NULL; int result = 0; if ( diskIsCDROM(gBootVolume) ) isCDROM = TRUE; else isCDROM = FALSE; // Allow user to override default timeout. if ( getIntForKey(kTimeoutKey, &timeout) == NO ) { /* 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; } // If user typed F8, abort quiet mode, // and display the menu. if (flushKeyboardBuffer()) { gBootMode &= ~kBootModeQuiet; timeout = 0; } clearBootArgs(); setCursorPosition( 0, 0, 0 ); clearScreenRows( 0, kScreenLastRow ); if ( ! ( gBootMode & kBootModeQuiet ) ) { // Display banner and show hardware info. printf( bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024 ); printVBEInfo(); } changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); verbose("Scanning device %x...", gBIOSDev); // Get a list of bootable volumes on the device. bvChain = scanBootVolumes( gBIOSDev, &bvCount ); gBootVolume = menuBVR = selectBootVolume( bvChain ); // When booting from CD, default to hard // drive boot when possible. if ( isCDROM ) { const char *val; char *prompt; int cnt; int optionKey; if (getValueForKey( kCDROMPromptKey, &val, &cnt )) { cnt += 1; prompt = malloc(cnt); strlcpy(prompt, val, cnt); } else { prompt = "Press any key to start up from CD-ROM, " "or press F8 to enter startup options."; cnt = 0; } if (getIntForKey( kCDROMOptionKey, &optionKey )) { // The key specified is a special key. } else if (getValueForKey( kCDROMOptionKey, &val, &cnt) && cnt >= 1) { optionKey = val[0]; } 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 (cnt) free(prompt); clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); if ( (key == 'h' || key == 'H') ) { // Boot from hard disk. // Scan the original device 0x80. BVRef hd_bvr = selectBootVolume(scanBootVolumes(0x80, 0)); if ( hd_bvr != NULL && hd_bvr->flags & kBVFlagNativeBoot ) { gBootVolume = hd_bvr; setRootVolume(hd_bvr); gBIOSDev = hd_bvr->biosdev; // FIXME: This skips the boot menu.. do we want to do that? goto done; } } else { if (optionKey < 0x100) key = key & 0x5F; if (key != optionKey) goto done; } gBootMode &= ~kBootModeQuiet; timeout = 0; } 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(gDisplayAllBootVolumesInMenu) menuItems = buildMenuItemsForAllDevices(gBIOSDev, &bvCount, menuBVR, &selectIndex, true); else #if 0 menuItems = buildMenuItemsForDevice(gBIOSDev, &bvCount, menuBVR, &selectIndex); #else menuItems = buildMenuItemsForAllDevices(gBIOSDev, &bvCount, menuBVR, &selectIndex, false); #endif // Clear screen and hide the blinking cursor. clearScreenRows( kMenuTopRow, kMenuTopRow + 2 ); changeCursor( 0, kMenuTopRow, kCursorTypeHidden, 0 ); nextRow = kMenuTopRow; showPrompt = YES; // Show the menu. if ( menuItems != NULL ) { printf("Use \30\31 keys to select the startup volume."); showMenu( menuItems, bvCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems ); nextRow += min( bvCount, kMenuMaxItems ) + 3; } // Show the boot prompt. showPrompt = (menuItems == NULL) || (menuBVR->flags & kBVFlagNativeBoot); showBootPrompt( nextRow, showPrompt ); do { key = getc(); updateMenu( key, (void **) &menuBVR ); newShowPrompt = (menuItems == NULL) || (menuBVR->flags & kBVFlagNativeBoot); if ( newShowPrompt != showPrompt ) { showPrompt = newShowPrompt; showBootPrompt( nextRow, showPrompt ); } if ( showPrompt ) updateBootArgs( key ); switch ( key & kASCIIKeyMask ) { case kReturnKey: if ( *gBootArgs == '?' ) { if ( strcmp( gBootArgs, "?video" ) == 0 ) { printVBEModeInfo(); } else if ( strcmp( gBootArgs, "?clocks") == 0 ) { printFrequencyInfo(); } else if ( strcmp( gBootArgs, "?memory" ) == 0 ) { printMemoryInfo(); } else { showHelp(); } key = 0; showBootPrompt( nextRow, showPrompt ); break; } else if ( *gBootArgs == '!' ) { if ( strcmp( gBootArgs, "!showall" ) == 0 ) { gDisplayAllBootVolumesInMenu = true; result = -1; // Make caller call us again. goto done; } else if ( strcmp( gBootArgs, "!showboot" ) == 0 ) { gDisplayAllBootVolumesInMenu = false; result = -1; // Make caller call us again. goto done; } else { showHelp(); } key = 0; showBootPrompt( nextRow, showPrompt ); break; } gBootVolume = menuBVR; break; case kEscapeKey: // Apple behavior: Leave gBootVolume as is which causes the default choice to be booted // New behavior: Clear gBootVolume to restart the loop. gBootVolume = NULL; clearBootArgs(); break; default: key = 0; } } while ( 0 == key ); done: firstRun = NO; clearScreenRows( kMenuTopRow, kScreenLastRow ); changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 ); if ( menuItems ) free(menuItems); return result; } //========================================================================== extern unsigned char chainbootdev; extern unsigned char chainbootflag; BOOL copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP) { int argLen = argName ? strlen(argName) : 0; int len = argLen + cnt + 1; // +1 to account for space if (len > *cntRemainingP) { error("Warning: boot arguments too long, truncating\n"); return NO; } if (argName) { strncpy( *argP, argName, argLen ); *argP += argLen; *argP[0] = '='; (*argP)++; len++; // +1 to account for '=' } strncpy( *argP, val, cnt ); *argP += cnt; *argP[0] = ' '; (*argP)++; *cntRemainingP -= len; return YES; } // // Returns TRUE if an argument was copied, FALSE otherwise BOOL processBootArgument( const char *argName, // The argument to search for const char *userString, // Typed-in boot arguments const char *kernelFlags, // Kernel flags from config table const char *configTable, char **argP, // Output value int *cntRemainingP, // Output count char *foundVal // found value ) { const char *val; int cnt; BOOL found = NO; if (getValueForBootKey(userString, argName, &val, &cnt)) { // Don't copy; these values will be copied at the end of argument processing. found = YES; } else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) { // Don't copy; these values will be copied at the end of argument processing. found = YES; } else if (getValueForConfigTableKey(configTable, argName, &val, &cnt)) { copyArgument(argName, val, cnt, argP, cntRemainingP); found = YES; } if (found && foundVal) { strlcpy(foundVal, val, cnt+1); } return found; } // Maximum config table value size #define VALUE_SIZE 1024 int processBootOptions() { const char * cp = gBootArgs; const char * val = 0; const char * kernel; int cnt; int userCnt; int cntRemaining; char * argP; char uuidStr[64]; BOOL uuidSet = NO; char * configKernelFlags; char * valueBuffer; valueBuffer = (char *)malloc(VALUE_SIZE); skipblanks( &cp ); // Update the unit and partition number. if ( gBootVolume ) { if ( gBootVolume->flags & kBVFlagForeignBoot ) { 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; // Load config table specified by the user, or use the default. if (getValueForBootKey( cp, "config", &val, &cnt ) == FALSE) { val = 0; cnt = 0; } loadSystemConfig(val, cnt); if ( !sysConfigValid ) return -1; // 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 = NO; if (( kernel = extractKernelName((char **)&cp) )) { strcpy( bootInfo->bootFile, kernel ); gOverrideKernel = YES; } else { if ( getValueForKey( kKernelNameKey, &val, &cnt ) ) { strlcpy( bootInfo->bootFile, val, cnt+1 ); if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) { gOverrideKernel = YES; } } else { strcpy( bootInfo->bootFile, kDefaultKernel ); } } cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space argP = bootArgs->CommandLine; // Get config table kernel flags, if not ignored. if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) == TRUE || getValueForKey( kKernelFlagsKey, &val, &cnt ) == FALSE) { val = ""; cnt = 0; } configKernelFlags = (char *)malloc(cnt + 1); strlcpy(configKernelFlags, val, cnt + 1); if (processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, 0)) { // boot-uuid was set either on the command-line // or in the config file. uuidSet = YES; } else { if (GetFSUUID(bootInfo->bootFile, uuidStr) == 0) { verbose("Setting boot-uuid to: %s\n", uuidStr); copyArgument(kBootUUIDKey, uuidStr, strlen(uuidStr), &argP, &cntRemaining); uuidSet = YES; } } if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gRootDevice)) { cnt = 0; if ( getValueForKey( kBootDeviceKey, &val, &cnt)) { valueBuffer[0] = '*'; cnt++; strlcpy(valueBuffer + 1, val, cnt); val = valueBuffer; } else { if (uuidSet) { 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)); } if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gPlatformName)) { 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(getValueForKey("arch", &val, &cnt)) { if(strncmp("i386", val, cnt) == 0 && cnt==4) gDesiredCpuType = CPU_TYPE_I386; else if(strncmp("x86_64", val, cnt) == 0 && cnt==6) gDesiredCpuType = CPU_TYPE_X86_64; else { printf("Unknown arch=%s", val); gDesiredCpuType = CPU_TYPE_ANY; } } else gDesiredCpuType = CPU_TYPE_ANY; // Don't check -legacy. That is a hack as it prevents you from loading K32 with 64-bit support. #if 0 if(getValueForKey(kLegacyFlag, &val, &cnt)) gDesiredCpuType = CPU_TYPE_I386; else gDesiredCpuType = CPU_TYPE_ANY; #endif gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt ) || getValueForKey( kSingleUserModeFlag, &val, &cnt ); gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt ) ) ? kBootModeSafe : kBootModeNormal; if ( getValueForKey( kOldSafeModeFlag, &val, &cnt ) ) { gBootMode = kBootModeSafe; } if ( getValueForKey( kMKextCacheKey, &val, &cnt ) ) { strlcpy(gMKextName, val, cnt + 1); } free(configKernelFlags); free(valueBuffer); return 0; } //========================================================================== // Load the help file and display the file contents on the screen. static void showHelp() { #define BOOT_HELP_PATH "/usr/standalone/i386/BootHelp.txt" int fd; int size; int line; int line_offset; int c; if ( (fd = open(BOOT_HELP_PATH, 0)) >= 0 ) { char * buffer; char * bp; size = file_size(fd); buffer = malloc( size + 1 ); read(fd, buffer, size); close(fd); bp = buffer; while (size > 0) { while (*bp != '\n') { bp++; size--; } *bp++ = '\0'; size--; } *bp = '\1'; line_offset = 0; setActiveDisplayPage(1); while (1) { clearScreenRows(0, 24); setCursorPosition(0, 0, 1); bp = buffer; for (line = 0; *bp != '\1' && line < line_offset; line++) { while (*bp != '\0') bp++; bp++; } for (line = 0; *bp != '\1' && line < 23; line++) { setCursorPosition(0, line, 1); printf("%s\n", bp); while (*bp != '\0') bp++; bp++; } setCursorPosition(0, 23, 1); if (*bp == '\1') { printf("[Type %sq or space to quit help]", (line_offset > 0) ? "p for previous page, " : ""); } else { printf("[Type %s%sq to quit help]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : ""); } c = getc(); if (c == 'q' || c == 'Q') { break; } if ((c == 'p' || c == 'P') && line_offset > 0) { line_offset -= 23; } if (c == ' ') { if (*bp == '\1') { break; } else { line_offset += 23; } } } free(buffer); setActiveDisplayPage(0); } } static inline int isHexDigit(char ch) { return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f'); } // This is a very simplistic prompting scheme that just grabs two hex characters // Eventually we need to do something more user-friendly like display a menu // based off of the Multiboot device list int selectAlternateBootDevice(int bootdevice) { int digitsI = 0; char digits[3] = {0,0,0}; // We've already printed the current boot device so user knows what it is printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n"); printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice); int key = 0; do { key = getc(); switch(key & kASCIIKeyMask) { case kBackspaceKey: if(digitsI > 0) { int x, y, t; getCursorPositionAndType(&x, &y, &t); // Assume x is not 0; x--; setCursorPosition(x,y,0); // back up one char // Overwrite with space without moving cursor position putca(' ', 0x07, 1); digitsI--; } else { // TODO: Beep or something } break; case kReturnKey: if(1) { digits[digitsI] = '\0'; char *end; int newbootdevice = strtol(digits, &end, 16); if(end == digits && *end == '\0') // User entered empty string { printf("\nUsing default boot device %x\n", bootdevice); key = 0; } else if(end != digits && *end == '\0') { bootdevice = newbootdevice; printf("\n"); key = 0; // We gots da boot device } else { printf("\nCouldn't parse. try again: "); digitsI = 0; } } break; default: if( isHexDigit(key & kASCIIKeyMask) && digitsI < 2 ) { putc(key & kASCIIKeyMask); digits[digitsI++] = key & kASCIIKeyMask; } else { // TODO: Beep or something } }; } while(key != 0); return bootdevice; }