]>
git.saurik.com Git - apple/boot.git/blob - i386/boot2/options.c
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Portions Copyright (c) 1999-2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 2.0 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
26 #include "bootstruct.h"
42 static void showHelp();
44 //==========================================================================
52 static void changeCursor( int col
, int row
, int type
, CursorState
* cs
)
54 if (cs
) getCursorPositionAndType( &cs
->x
, &cs
->y
, &cs
->type
);
55 setCursorType( type
);
56 setCursorPosition( col
, row
, 0 );
59 static void moveCursor( int col
, int row
)
61 setCursorPosition( col
, row
, 0 );
64 static void restoreCursor( const CursorState
* cs
)
66 setCursorPosition( cs
->x
, cs
->y
, 0 );
67 setCursorType( cs
->type
);
70 //==========================================================================
72 /* Flush keyboard buffer; returns TRUE if any of the flushed
76 static BOOL
flushKeyboardBuffer()
80 while ( readKeyboardStatus() ) {
81 if (bgetc() == 0x4200) status
= TRUE
;
86 //==========================================================================
88 static int countdown( const char * msg
, int row
, int timeout
)
92 int col
= strlen(msg
) + 1;
94 flushKeyboardBuffer();
99 for ( time
= time18(), timeout
++; timeout
> 0; )
101 if (ch
= readKeyboardStatus())
104 // Count can be interrupted by holding down shift,
105 // control or alt key
106 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) {
111 if ( time18() >= time
)
115 moveCursor( col
, row
);
116 printf("(%d) ", timeout
);
120 flushKeyboardBuffer();
125 //==========================================================================
127 static char gBootArgs
[BOOT_STRING_LEN
];
128 static char * gBootArgsPtr
= gBootArgs
;
129 static char * gBootArgsEnd
= gBootArgs
+ BOOT_STRING_LEN
- 1;
131 static void clearBootArgs()
133 gBootArgsPtr
= gBootArgs
;
134 memset( gBootArgs
, '\0', BOOT_STRING_LEN
);
137 //==========================================================================
139 static void showBootPrompt( int row
, BOOL visible
)
141 extern char bootPrompt
[];
143 changeCursor( 0, row
, kCursorTypeUnderline
, 0 );
144 clearScreenRows( row
, kScreenLastRow
);
149 printf( bootPrompt
);
153 printf("Press Enter to start up the foreign OS. ");
157 //==========================================================================
159 static void updateBootArgs( int key
)
161 key
&= kASCIIKeyMask
;
166 if ( gBootArgsPtr
> gBootArgs
)
169 getCursorPositionAndType( &x
, &y
, &t
);
175 setCursorPosition( x
, y
, 0 );
177 *gBootArgsPtr
-- = '\0';
182 if ( key
>= ' ' && gBootArgsPtr
< gBootArgsEnd
)
184 putchar(key
); // echo to screen
185 *gBootArgsPtr
++ = key
;
191 //==========================================================================
198 static const MenuItem
* gMenuItems
= NULL
;
199 static int gMenuItemCount
;
201 static int gMenuHeight
;
203 static int gMenuBottom
;
204 static int gMenuSelection
;
206 static void printMenuItem( const MenuItem
* item
, int highlight
)
211 putca(' ', 0x70, strlen(item
->name
) + 4);
213 putca(' ', 0x07, 40);
215 printf(" %40s\n", item
->name
);
218 //==========================================================================
220 static void showMenu( const MenuItem
* items
, int count
,
221 int selection
, int row
, int height
)
224 CursorState cursorState
;
226 if ( items
== NULL
|| count
== 0 ) return;
228 // head and tail points to the start and the end of the list.
229 // top and bottom points to the first and last visible items
230 // in the menu window.
234 gMenuHeight
= height
;
235 gMenuItemCount
= count
;
237 gMenuBottom
= min( count
, height
) - 1;
238 gMenuSelection
= selection
;
240 // If the selected item is not visible, shift the list down.
242 if ( gMenuSelection
> gMenuBottom
)
244 gMenuTop
+= ( gMenuSelection
- gMenuBottom
);
245 gMenuBottom
= gMenuSelection
;
248 // Draw the visible items.
250 changeCursor( 0, row
, kCursorTypeHidden
, &cursorState
);
252 for ( i
= gMenuTop
; i
<= gMenuBottom
; i
++ )
254 printMenuItem( &items
[i
], (i
== gMenuSelection
) );
257 restoreCursor( &cursorState
);
260 //==========================================================================
262 static int updateMenu( int key
, void ** paramPtr
)
277 if ( NULL
== gMenuItems
) return 0;
279 // Look at the scan code.
283 case 0x4800: // Up Arrow
284 if ( gMenuSelection
!= gMenuTop
)
285 draw
.f
.selectionUp
= 1;
286 else if ( gMenuTop
> 0 )
287 draw
.f
.scrollDown
= 1;
290 case 0x5000: // Down Arrow
291 if ( gMenuSelection
!= gMenuBottom
)
292 draw
.f
.selectionDown
= 1;
293 else if ( gMenuBottom
< (gMenuItemCount
- 1) )
300 if ( draw
.f
.scrollUp
)
302 scollPage(0, gMenuRow
, 40, gMenuRow
+ gMenuHeight
- 1, 0x07, 1, 1);
303 gMenuTop
++; gMenuBottom
++;
304 draw
.f
.selectionDown
= 1;
307 if ( draw
.f
.scrollDown
)
309 scollPage(0, gMenuRow
, 40, gMenuRow
+ gMenuHeight
- 1, 0x07, 1, -1);
310 gMenuTop
--; gMenuBottom
--;
311 draw
.f
.selectionUp
= 1;
314 if ( draw
.f
.selectionUp
|| draw
.f
.selectionDown
)
316 CursorState cursorState
;
318 // Set cursor at current position, and clear inverse video.
320 changeCursor( 0, gMenuRow
+ gMenuSelection
- gMenuTop
,
321 kCursorTypeHidden
, &cursorState
);
323 printMenuItem( &gMenuItems
[gMenuSelection
], 0 );
325 if ( draw
.f
.selectionUp
) gMenuSelection
--;
326 else gMenuSelection
++;
328 moveCursor( 0, gMenuRow
+ gMenuSelection
- gMenuTop
);
330 printMenuItem( &gMenuItems
[gMenuSelection
], 1 );
332 restoreCursor( &cursorState
);
335 *paramPtr
= gMenuItems
[gMenuSelection
].param
;
342 //==========================================================================
344 static void skipblanks( const char ** cpp
)
346 while ( **(cpp
) == ' ' || **(cpp
) == '\t' ) ++(*cpp
);
349 //==========================================================================
351 static const char * extractKernelName( char ** cpp
)
357 // Convert char to lower case.
361 // Must start with a letter or a '/'.
363 if ( (c
< 'a' || c
> 'z') && ( c
!= '/' ) )
366 // Keep consuming characters until we hit a separator.
368 while ( *cp
&& (*cp
!= '=') && (*cp
!= ' ') && (*cp
!= '\t') )
371 // Only SPACE or TAB separator is accepted.
372 // Reject everything else.
377 // Overwrite the separator, and move the pointer past
380 if (*cp
!= '\0') *cp
++ = '\0';
386 //==========================================================================
389 printMemoryInfo(void)
393 MemoryRange
*mp
= bootInfo
->memoryMap
;
395 // Activate and clear page 1
396 setActiveDisplayPage(1);
397 clearScreenRows(0, 24);
398 setCursorPosition( 0, 0, 1 );
400 printf("BIOS reported memory ranges:\n");
402 for (i
=0; i
<bootInfo
->memoryMapCount
; i
++) {
403 printf("Base 0x%08x%08x, ",
404 (unsigned long)(mp
->base
>> 32),
405 (unsigned long)(mp
->base
));
406 printf("length 0x%08x%08x, type %d\n",
407 (unsigned long)(mp
->length
>> 32),
408 (unsigned long)(mp
->length
),
411 printf("(Press a key to continue...)");
418 printf("(Press a key to continue...)");
422 setActiveDisplayPage(0);
425 //==========================================================================
428 getBootOptions(BOOL firstRun
)
439 BOOL showPrompt
, newShowPrompt
, isCDROM
;
440 MenuItem
* menuItems
= NULL
;
442 if ( diskIsCDROM(gBootVolume
) )
447 // Allow user to override default timeout.
449 if ( getIntForKey(kTimeoutKey
, &timeout
) == NO
)
452 timeout
= kCDBootTimeout
;
454 timeout
= kBootTimeout
;
456 if (timeout
< 0) gBootMode
|= kBootModeQuiet
;
458 // If the user is holding down a modifier key,
460 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) {
461 gBootMode
|= kBootModeSafe
;
464 // If user typed F8, abort quiet mode,
465 // and display the menu.
466 if (flushKeyboardBuffer()) {
467 gBootMode
&= ~kBootModeQuiet
;
473 setCursorPosition( 0, 0, 0 );
474 clearScreenRows( 0, kScreenLastRow
);
475 if ( ! ( gBootMode
& kBootModeQuiet
) ) {
476 // Display banner and show hardware info.
477 printf( bootBanner
, (bootInfo
->convmem
+ bootInfo
->extmem
) / 1024 );
481 changeCursor( 0, kMenuTopRow
, kCursorTypeUnderline
, 0 );
483 verbose("Scanning device %x...", gBIOSDev
);
485 // Get a list of bootable volumes on the device.
487 bvChain
= scanBootVolumes( gBIOSDev
, &bvCount
);
488 gBootVolume
= menuBVR
= selectBootVolume( bvChain
);
490 // When booting from CD, default to hard
491 // drive boot when possible.
500 if (getValueForKey( kCDROMPromptKey
, &val
, &cnt
)) {
502 prompt
= malloc(cnt
);
503 strlcpy(prompt
, val
, cnt
);
505 prompt
= "Press any key to start up from CD-ROM, "
506 "or press F8 to enter startup options.";
510 if (getIntForKey( kCDROMOptionKey
, &optionKey
)) {
511 // The key specified is a special key.
512 } else if (getValueForKey( kCDROMOptionKey
, &val
, &cnt
) && cnt
>= 1) {
519 key
= countdown(prompt
, kMenuTopRow
, timeout
);
523 clearScreenRows( kMenuTopRow
, kMenuTopRow
+ 2 );
526 // Boot from hard disk.
527 // Scan the original device 0x80.
529 BVRef hd_bvr
= selectBootVolume(scanBootVolumes(0x80, 0));
530 if ( hd_bvr
->flags
& kBVFlagNativeBoot
) {
531 gBootVolume
= hd_bvr
;
532 gBIOSDev
= hd_bvr
->biosdev
;
533 initKernBootStruct( gBIOSDev
);
537 if (optionKey
< 0x100)
539 if (key
!= optionKey
)
542 gBootMode
&= ~kBootModeQuiet
;
546 if ( gBootMode
& kBootModeQuiet
)
548 // No input allowed from user.
552 if ( firstRun
&& ( timeout
> 0 ) &&
553 ( countdown("Press any key to enter startup options.",
554 kMenuTopRow
, timeout
) == 0 ) )
556 // If the user is holding down a modifier key,
558 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) {
559 gBootMode
|= kBootModeSafe
;
566 // Allocate memory for an array of menu items.
568 menuItems
= (MenuItem
*) malloc( sizeof(MenuItem
) * bvCount
);
569 if ( menuItems
== NULL
) goto done
;
571 // Associate a menu item for each BVRef.
573 for ( bvr
= bvChain
, i
= bvCount
- 1, selectIndex
= 0;
574 bvr
; bvr
= bvr
->next
, i
-- )
576 getBootVolumeDescription( bvr
, menuItems
[i
].name
, 80, YES
);
577 menuItems
[i
].param
= (void *) bvr
;
578 if ( bvr
== menuBVR
) selectIndex
= i
;
582 // Clear screen and hide the blinking cursor.
584 clearScreenRows( kMenuTopRow
, kMenuTopRow
+ 2 );
585 changeCursor( 0, kMenuTopRow
, kCursorTypeHidden
, 0 );
586 nextRow
= kMenuTopRow
;
593 printf("Use \30\31 keys to select the startup volume.");
594 showMenu( menuItems
, bvCount
, selectIndex
, kMenuTopRow
+ 2, kMenuMaxItems
);
595 nextRow
+= min( bvCount
, kMenuMaxItems
) + 3;
598 // Show the boot prompt.
600 showPrompt
= (bvCount
== 0) || (menuBVR
->flags
& kBVFlagNativeBoot
);
601 showBootPrompt( nextRow
, showPrompt
);
606 updateMenu( key
, (void **) &menuBVR
);
608 newShowPrompt
= (bvCount
== 0) ||
609 (menuBVR
->flags
& kBVFlagNativeBoot
);
611 if ( newShowPrompt
!= showPrompt
)
613 showPrompt
= newShowPrompt
;
614 showBootPrompt( nextRow
, showPrompt
);
616 if ( showPrompt
) updateBootArgs( key
);
618 switch ( key
& kASCIIKeyMask
)
621 if ( *gBootArgs
== '?' )
623 if ( strcmp( gBootArgs
, "?video" ) == 0 ) {
625 } else if ( strcmp( gBootArgs
, "?memory" ) == 0 ) {
631 showBootPrompt( nextRow
, showPrompt
);
634 gBootVolume
= menuBVR
;
650 clearScreenRows( kMenuTopRow
, kScreenLastRow
);
651 changeCursor( 0, kMenuTopRow
, kCursorTypeUnderline
, 0 );
653 if ( menuItems
) free(menuItems
);
658 //==========================================================================
660 extern unsigned char chainbootdev
;
661 extern unsigned char chainbootflag
;
664 copyArgument(const char *argName
, const char *val
, int cnt
, char **argP
, int *cntRemainingP
)
666 int argLen
= argName
? strlen(argName
) : 0;
667 int len
= argLen
+ cnt
+ 1; // +1 to account for space
669 if (len
> *cntRemainingP
) {
670 error("Warning: boot arguments too long, truncating\n");
675 strncpy( *argP
, argName
, argLen
);
679 len
++; // +1 to account for '='
681 strncpy( *argP
, val
, cnt
);
686 *cntRemainingP
-= len
;
690 // Returns TRUE if an argument was copied, FALSE otherwise
694 const char *argName
, // The argument to search for
695 const char *userString
, // Typed-in boot arguments
696 const char *kernelFlags
, // Kernel flags from config table
697 const char *configTable
,
698 char **argP
, // Output value
699 int *cntRemainingP
, // Output count
700 char *foundVal
// found value
707 if (getValueForBootKey(userString
, argName
, &val
, &cnt
)) {
708 // Don't copy; these values will be copied at the end of argument processing.
710 } else if (getValueForBootKey(kernelFlags
, argName
, &val
, &cnt
)) {
711 // Don't copy; these values will be copied at the end of argument processing.
713 } else if (getValueForConfigTableKey(configTable
, argName
, &val
, &cnt
)) {
714 copyArgument(argName
, val
, cnt
, argP
, cntRemainingP
);
717 if (found
&& foundVal
) {
718 strlcpy(foundVal
, val
, cnt
+1);
723 // Maximum config table value size
724 #define VALUE_SIZE 1024
729 const char * cp
= gBootArgs
;
730 const char * val
= 0;
738 char * configKernelFlags
;
741 valueBuffer
= (char *)malloc(VALUE_SIZE
);
745 // Update the unit and partition number.
749 if ( gBootVolume
->flags
& kBVFlagForeignBoot
)
751 readBootSector( gBootVolume
->biosdev
, gBootVolume
->part_boff
,
755 // Setup edx, and signal intention to chain load the
759 chainbootdev
= gBootVolume
->biosdev
;
765 bootInfo
->kernDev
&= ~((B_UNITMASK
<< B_UNITSHIFT
) |
766 (B_PARTITIONMASK
<< B_PARTITIONSHIFT
));
768 bootInfo
->kernDev
|= MAKEKERNDEV( 0,
769 /* unit */ BIOS_DEV_UNIT(gBootVolume
),
770 /* partition */ gBootVolume
->part_no
);
773 // Load config table specified by the user, or use the default.
775 if (getValueForBootKey( cp
, "config", &val
, &cnt
) == FALSE
) {
779 loadSystemConfig(val
, cnt
);
780 if ( !sysConfigValid
) return -1;
782 // Use the kernel name specified by the user, or fetch the name
783 // in the config table, or use the default if not specified.
784 // Specifying a kernel name on the command line, or specifying
785 // a non-default kernel name in the config file counts as
786 // overriding the kernel, which causes the kernelcache not
789 gOverrideKernel
= NO
;
790 if (( kernel
= extractKernelName((char **)&cp
) )) {
791 strcpy( bootInfo
->bootFile
, kernel
);
792 gOverrideKernel
= YES
;
794 if ( getValueForKey( kKernelNameKey
, &val
, &cnt
) ) {
795 strlcpy( bootInfo
->bootFile
, val
, cnt
+1 );
796 if (strcmp( bootInfo
->bootFile
, kDefaultKernel
) != 0) {
797 gOverrideKernel
= YES
;
800 strcpy( bootInfo
->bootFile
, kDefaultKernel
);
804 cntRemaining
= BOOT_STRING_LEN
- 2; // save 1 for NULL, 1 for space
805 argP
= bootArgs
->CommandLine
;
807 // Get config table kernel flags, if not ignored.
808 if (getValueForBootKey(cp
, kIgnoreBootFileFlag
, &val
, &cnt
) == TRUE
||
809 getValueForKey( kKernelFlagsKey
, &val
, &cnt
) == FALSE
) {
813 configKernelFlags
= (char *)malloc(cnt
+ 1);
814 strlcpy(configKernelFlags
, val
, cnt
+ 1);
816 if (processBootArgument(kBootUUIDKey
, cp
, configKernelFlags
, bootInfo
->config
, &argP
, &cntRemaining
, 0)) {
817 // boot-uuid was set either on the command-line
818 // or in the config file.
821 if (GetFSUUID(bootInfo
->bootFile
, uuidStr
) == 0) {
822 verbose("Setting boot-uuid to: %s\n", uuidStr
);
823 copyArgument(kBootUUIDKey
, uuidStr
, strlen(uuidStr
), &argP
, &cntRemaining
);
828 if (!processBootArgument(kRootDeviceKey
, cp
, configKernelFlags
, bootInfo
->config
, &argP
, &cntRemaining
, gRootDevice
)) {
830 if ( getValueForKey( kBootDeviceKey
, &val
, &cnt
)) {
831 valueBuffer
[0] = '*';
833 strlcpy(valueBuffer
+ 1, val
, cnt
);
840 // Don't set "rd=.." if there is no boot device key
847 copyArgument( kRootDeviceKey
, val
, cnt
, &argP
, &cntRemaining
);
849 strlcpy( gRootDevice
, val
, (cnt
+ 1));
852 if (!processBootArgument(kPlatformKey
, cp
, configKernelFlags
, bootInfo
->config
, &argP
, &cntRemaining
, gPlatformName
)) {
853 getPlatformName(gPlatformName
);
854 copyArgument(kPlatformKey
, gPlatformName
, strlen(gPlatformName
), &argP
, &cntRemaining
);
857 if (!getValueForBootKey(cp
, kSafeModeFlag
, &val
, &cnt
) &&
858 !getValueForBootKey(configKernelFlags
, kSafeModeFlag
, &val
, &cnt
)) {
859 if (gBootMode
& kBootModeSafe
) {
860 copyArgument(0, kSafeModeFlag
, strlen(kSafeModeFlag
), &argP
, &cntRemaining
);
864 // Store the merged kernel flags and boot args.
866 cnt
= strlen(configKernelFlags
);
868 if (cnt
> cntRemaining
) {
869 error("Warning: boot arguments too long, truncating\n");
872 strncpy(argP
, configKernelFlags
, cnt
);
876 userCnt
= strlen(cp
);
877 if (userCnt
> cntRemaining
) {
878 error("Warning: boot arguments too long, truncating\n");
879 userCnt
= cntRemaining
;
881 strncpy(&argP
[cnt
], cp
, userCnt
);
882 argP
[cnt
+userCnt
] = '\0';
884 gVerboseMode
= getValueForKey( kVerboseModeFlag
, &val
, &cnt
) ||
885 getValueForKey( kSingleUserModeFlag
, &val
, &cnt
);
887 gBootMode
= ( getValueForKey( kSafeModeFlag
, &val
, &cnt
) ) ?
888 kBootModeSafe
: kBootModeNormal
;
890 if ( getValueForKey( kOldSafeModeFlag
, &val
, &cnt
) ) {
891 gBootMode
= kBootModeSafe
;
894 if ( getValueForKey( kMKextCacheKey
, &val
, &cnt
) ) {
895 strlcpy(gMKextName
, val
, cnt
+ 1);
898 free(configKernelFlags
);
905 //==========================================================================
906 // Load the help file and display the file contents on the screen.
908 static void showHelp()
910 #define BOOT_HELP_PATH "/usr/standalone/i386/BootHelp.txt"
918 if ( (fd
= open(BOOT_HELP_PATH
, 0)) >= 0 )
923 size
= file_size(fd
);
924 buffer
= malloc( size
+ 1 );
925 read(fd
, buffer
, size
);
930 while (*bp
!= '\n') {
940 setActiveDisplayPage(1);
943 clearScreenRows(0, 24);
944 setCursorPosition(0, 0, 1);
946 for (line
= 0; *bp
!= '\1' && line
< line_offset
; line
++) {
947 while (*bp
!= '\0') bp
++;
950 for (line
= 0; *bp
!= '\1' && line
< 23; line
++) {
951 setCursorPosition(0, line
, 1);
953 while (*bp
!= '\0') bp
++;
957 setCursorPosition(0, 23, 1);
959 printf("[Type %sq or space to quit help]",
960 (line_offset
> 0) ? "p for previous page, " : "");
962 printf("[Type %s%sq to quit help]",
963 (line_offset
> 0) ? "p for previous page, " : "",
964 (*bp
!= '\1') ? "space for next page, " : "");
968 if (c
== 'q' || c
== 'Q') {
971 if ((c
== 'p' || c
== 'P') && line_offset
> 0) {
984 setActiveDisplayPage(0);