Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/boot2/options.c

1/*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
12 * this file.
13 *
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
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include "boot.h"
26#include "bootstruct.h"
27#include "fdisk.h"
28#include "ramdisk.h"
29#include "gui.h"
30#ifdef EMBED_THEME
31#include "embedded.h"
32#endif
33
34int shouldboot=0;
35
36extern int multiboot_timeout;
37extern int multiboot_timeout_set;
38
39extern BVRef bvChain;
40//extern intmenucount;
41
42extern intgDeviceCount;
43
44intselectIndex = 0;
45MenuItem * menuItems = NULL;
46
47enum {
48 kMenuTopRow = 5,
49 kMenuMaxItems = 10,
50 kScreenLastRow = 24
51};
52
53//==========================================================================
54
55typedef struct {
56 int x;
57 int y;
58 int type;
59} CursorState;
60
61static void changeCursor( int col, int row, int type, CursorState * cs )
62{
63 if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type );
64 setCursorType( type );
65 setCursorPosition( col, row, 0 );
66}
67
68static void moveCursor( int col, int row )
69{
70 setCursorPosition( col, row, 0 );
71}
72
73static void restoreCursor( const CursorState * cs )
74{
75 setCursorPosition( cs->x, cs->y, 0 );
76 setCursorType( cs->type );
77}
78
79//==========================================================================
80
81/* Flush keyboard buffer; returns TRUE if any of the flushed
82 * characters was F8.
83 */
84
85static BOOL flushKeyboardBuffer()
86{
87 BOOL status = FALSE;
88
89 while ( readKeyboardStatus() ) {
90 if (bgetc() == 0x4200) status = TRUE;
91 }
92 return status;
93}
94
95//==========================================================================
96
97static int countdown( const char * msg, int row, int timeout )
98{
99 unsigned long time;
100 int ch = 0;
101 int col = strlen(msg) + 1;
102
103 flushKeyboardBuffer();
104
105if( bootArgs->Video.v_display == VGA_TEXT_MODE )
106{
107moveCursor( 0, row );
108printf(msg);
109
110} else {
111
112position_t p = pos( gui.screen.width / 2 + 1 , ( gui.devicelist.pos.y + 3 ) + ( ( gui.devicelist.height - gui.devicelist.iconspacing ) / 2 ) );
113
114char dummy[80];
115getBootVolumeDescription( gBootVolume, dummy, 80, YES );
116drawDeviceIcon( gBootVolume, gui.screen.pixmap, p );
117drawStrCenteredAt( (char *) msg, &font_small, gui.screen.pixmap, gui.countdown.pos );
118
119// make this screen the new background
120memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
121
122}
123
124int multi_buff = 18 * (timeout);
125 int multi = ++multi_buff;
126
127 int lasttime=0;
128
129 for ( time = time18(), timeout++; timeout > 0; )
130 {
131if( time18() > lasttime)
132{
133multi--;
134lasttime=time18();
135}
136
137 if (ch = readKeyboardStatus())
138 break;
139
140 // Count can be interrupted by holding down shift,
141 // control or alt key
142 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 )
143{
144 ch = 1;
145 break;
146 }
147
148 if ( time18() >= time )
149 {
150 time += 18;
151 timeout--;
152
153if( bootArgs->Video.v_display == VGA_TEXT_MODE )
154{
155moveCursor( col, row );
156printf("(%d) ", timeout);
157}
158 }
159
160if( bootArgs->Video.v_display == GRAPHICS_MODE )
161{
162drawProgressBar( gui.screen.pixmap, 100, gui.progressbar.pos , ( multi * 100 / multi_buff ) );
163gui.redraw = YES;
164updateVRAM();
165}
166
167 }
168
169 flushKeyboardBuffer();
170
171 return ch;
172}
173
174//==========================================================================
175
176static char gBootArgs[BOOT_STRING_LEN];
177static char * gBootArgsPtr = gBootArgs;
178static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1;
179static char booterCommand[BOOT_STRING_LEN];
180static char booterParam[BOOT_STRING_LEN];
181
182static void clearBootArgs()
183{
184 gBootArgsPtr = gBootArgs;
185 memset( gBootArgs, '\0', BOOT_STRING_LEN );
186
187if( bootArgs->Video.v_display == GRAPHICS_MODE )
188clearGraphicBootPrompt();
189
190}
191
192//==========================================================================
193
194static void showBootPrompt( int row, BOOL visible )
195{
196 extern char bootPrompt[];
197 extern char bootRescanPrompt[];
198
199if( bootArgs->Video.v_display == VGA_TEXT_MODE )
200 {
201changeCursor( 0, row, kCursorTypeUnderline, 0 );
202clearScreenRows( row, kScreenLastRow );
203 }
204
205 clearBootArgs();
206
207 if ( visible )
208 {
209if( bootArgs->Video.v_display == VGA_TEXT_MODE )
210if (gEnableCDROMRescan)
211printf( bootRescanPrompt );
212else
213printf( bootPrompt );
214 }
215 else
216 {
217if( bootArgs->Video.v_display == GRAPHICS_MODE )
218clearGraphicBootPrompt();
219else
220printf("Press Enter to start up the foreign OS. ");
221
222 }
223}
224
225//==========================================================================
226
227static void updateBootArgs( int key )
228{
229 key &= kASCIIKeyMask;
230
231 switch ( key )
232 {
233 case kBackspaceKey:
234 if ( gBootArgsPtr > gBootArgs )
235 {
236 int x, y, t;
237 getCursorPositionAndType( &x, &y, &t );
238 if ( x == 0 && y )
239 {
240 x = 80; y--;
241 }
242 if (x)
243x--;
244if( bootArgs->Video.v_display == VGA_TEXT_MODE )
245{
246setCursorPosition( x, y, 0 );
247putca(' ', 0x07, 1);
248} else
249updateGraphicBootPrompt(kBackspaceKey);
250
251*gBootArgsPtr-- = '\0';
252}
253
254break;
255
256 default:
257 if ( key >= ' ' && gBootArgsPtr < gBootArgsEnd)
258 {
259if( bootArgs->Video.v_display == VGA_TEXT_MODE )
260putchar(key); // echo to screen
261else
262updateGraphicBootPrompt(key);
263*gBootArgsPtr++ = key;
264}
265
266break;
267 }
268}
269
270//==========================================================================
271
272static const MenuItem * gMenuItems = NULL;
273
274static int gMenuItemCount;
275static int gMenuRow;
276static int gMenuHeight;
277static int gMenuTop;
278static int gMenuBottom;
279static int gMenuSelection;
280
281static int gMenuStart;
282static int gMenuEnd;
283
284static void printMenuItem( const MenuItem * item, int highlight )
285{
286 printf(" ");
287
288 if ( highlight )
289 putca(' ', 0x70, strlen(item->name) + 4);
290 else
291 putca(' ', 0x07, 40);
292
293 printf(" %40s\n", item->name);
294}
295
296//==========================================================================
297
298static void showMenu( const MenuItem * items, int count,
299 int selection, int row, int height )
300{
301 int i;
302 CursorState cursorState;
303
304 if ( items == NULL || count == 0 )
305return;
306
307 // head and tail points to the start and the end of the list.
308 // top and bottom points to the first and last visible items
309 // in the menu window.
310
311 gMenuItems= items;
312 gMenuRow= row;
313 gMenuHeight= height;
314 gMenuItemCount= count;
315 gMenuTop= 0;
316 gMenuBottom= min( count, height ) - 1;
317 gMenuSelection= selection;
318
319 gMenuStart= 0;
320 gMenuEnd = min( count, gui.maxdevices ) - 1;
321
322// If the selected item is not visible, shift the list down.
323
324 if ( gMenuSelection > gMenuBottom )
325 {
326 gMenuTop += ( gMenuSelection - gMenuBottom );
327 gMenuBottom = gMenuSelection;
328 }
329
330if ( gMenuSelection > gMenuEnd )
331 {
332gMenuStart += ( gMenuSelection - gMenuEnd );
333 gMenuEnd = gMenuSelection;
334 }
335
336// Draw the visible items.
337
338if( bootArgs->Video.v_display == GRAPHICS_MODE )
339
340drawDeviceList(gMenuStart, gMenuEnd, gMenuSelection);
341
342else {
343
344changeCursor( 0, row, kCursorTypeHidden, &cursorState );
345
346for ( i = gMenuTop; i <= gMenuBottom; i++ )
347{
348printMenuItem( &items[i], (i == gMenuSelection) );
349}
350
351restoreCursor( &cursorState );
352 }
353}
354
355//==========================================================================
356
357static int updateMenu( int key, void ** paramPtr )
358{
359 int moved = 0;
360
361 union {
362 struct {
363 unsigned int
364 selectionUp : 1,
365 selectionDown : 1,
366 scrollUp : 1,
367 scrollDown : 1;
368 } f;
369 unsigned int w;
370 } draw = {{0}};
371
372 if ( gMenuItems == NULL )
373return 0;
374
375if( bootArgs->Video.v_display == GRAPHICS_MODE )
376{
377int res;
378
379// set navigation keys for horizontal layout as defaults
380int previous= 0x4B00;// left arrow
381int subsequent= 0x4D00;// right arrow
382int menu= 0x5000;// down arrow
383
384if ( gui.layout == VerticalLayout )
385{
386// set navigation keys for vertical layout
387previous= 0x4800;// up arrow
388subsequent= 0x5000;// down arrow
389menu= 0x4B00;// right arrow
390}
391
392if ( key == previous )
393{
394if ( gMenuSelection > gMenuTop )
395draw.f.selectionUp = 1;
396else if ( gMenuTop > 0 )
397draw.f.scrollDown = 1;
398
399}
400
401else if ( key == subsequent )
402{
403if ( gMenuSelection != gMenuBottom)
404draw.f.selectionDown = 1;
405else if ( gMenuBottom < ( gMenuItemCount - 1 ) )
406draw.f.scrollUp = 1;
407}
408
409else if ( key == menu )
410{
411if ( gui.menu.draw )
412updateInfoMenu(key);
413else
414drawInfoMenu();
415}
416
417else if ( gui.menu.draw )
418{
419res = updateInfoMenu(key);
420
421if ( res == CLOSE_INFO_MENU )
422gui.menu.draw = NO;
423else
424{
425shouldboot = ( res != DO_NOT_BOOT );
426
427if ( shouldboot )
428gui.menu.draw = NO;
429
430switch (res)
431{
432case BOOT_NORMAL:
433gVerboseMode = NO;
434gBootMode = kBootModeNormal;
435break;
436
437case BOOT_VERBOSE:
438gVerboseMode = YES;
439gBootMode = kBootModeNormal;
440*gBootArgsPtr++ = '-';
441*gBootArgsPtr++ = 'v';
442break;
443
444case BOOT_IGNORECACHE:
445gVerboseMode = NO;
446gBootMode = kBootModeNormal;
447*gBootArgsPtr++ = '-';
448*gBootArgsPtr++ = 'f';
449break;
450
451case BOOT_SINGLEUSER:
452gVerboseMode = YES;
453gBootMode = kBootModeNormal;
454*gBootArgsPtr++ = '-';
455*gBootArgsPtr++ = 's';
456break;
457}
458
459}
460
461}
462
463} else {
464switch ( key )
465{
466 case 0x4800: // Up Arrow
467if ( gMenuSelection != gMenuTop )
468draw.f.selectionUp = 1;
469else if ( gMenuTop > 0 )
470draw.f.scrollDown = 1;
471break;
472
473case 0x5000: // Down Arrow
474if ( gMenuSelection != gMenuBottom )
475draw.f.selectionDown = 1;
476else if ( gMenuBottom < (gMenuItemCount - 1) )
477draw.f.scrollUp = 1;
478break;
479}
480}
481
482 if ( draw.w )
483 {
484 if ( draw.f.scrollUp )
485 {
486 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, 1);
487 gMenuTop++; gMenuBottom++;
488gMenuStart++; gMenuEnd++;
489 draw.f.selectionDown = 1;
490 }
491
492 if ( draw.f.scrollDown )
493 {
494 scollPage(0, gMenuRow, 40, gMenuRow + gMenuHeight - 1, 0x07, 1, -1);
495 gMenuTop--; gMenuBottom--;
496 gMenuStart--; gMenuEnd--;
497 draw.f.selectionUp = 1;
498 }
499
500 if ( draw.f.selectionUp || draw.f.selectionDown )
501 {
502
503CursorState cursorState;
504
505// Set cursor at current position, and clear inverse video.
506
507if( bootArgs->Video.v_display == VGA_TEXT_MODE )
508{
509changeCursor( 0, gMenuRow + gMenuSelection - gMenuTop, kCursorTypeHidden, &cursorState );
510printMenuItem( &gMenuItems[gMenuSelection], 0 );
511}
512
513if ( draw.f.selectionUp )
514{
515gMenuSelection--;
516if(( gMenuSelection - gMenuStart) == -1 )
517{
518gMenuStart--;
519gMenuEnd--;
520}
521
522} else {
523gMenuSelection++;
524if(( gMenuSelection - ( gui.maxdevices - 1) - gMenuStart) > 0 )
525{
526gMenuStart++;
527gMenuEnd++;
528}
529 }
530
531if( bootArgs->Video.v_display == VGA_TEXT_MODE )
532 {
533moveCursor( 0, gMenuRow + gMenuSelection - gMenuTop );
534printMenuItem( &gMenuItems[gMenuSelection], 1 );
535restoreCursor( &cursorState );
536
537 } else
538
539drawDeviceList (gMenuStart, gMenuEnd, gMenuSelection);
540
541}
542
543 *paramPtr = gMenuItems[gMenuSelection].param;
544 moved = 1;
545 }
546
547return moved;
548}
549
550//==========================================================================
551
552static void skipblanks( const char ** cpp )
553{
554 while ( **(cpp) == ' ' || **(cpp) == '\t' ) ++(*cpp);
555}
556
557//==========================================================================
558
559static const char * extractKernelName( char ** cpp )
560{
561 char * kn = *cpp;
562 char * cp = *cpp;
563 char c;
564
565 // Convert char to lower case.
566
567 c = *cp | 0x20;
568
569 // Must start with a letter or a '/'.
570
571 if ( (c < 'a' || c > 'z') && ( c != '/' ) )
572 return 0;
573
574 // Keep consuming characters until we hit a separator.
575
576 while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') )
577 cp++;
578
579 // Only SPACE or TAB separator is accepted.
580 // Reject everything else.
581
582 if (*cp == '=')
583 return 0;
584
585 // Overwrite the separator, and move the pointer past
586 // the kernel name.
587
588 if (*cp != '\0') *cp++ = '\0';
589 *cpp = cp;
590
591 return kn;
592}
593
594//==========================================================================
595
596static void
597printMemoryInfo(void)
598{
599 int line;
600 int i;
601 MemoryRange *mp = bootInfo->memoryMap;
602
603 // Activate and clear page 1
604 setActiveDisplayPage(1);
605 clearScreenRows(0, 24);
606 setCursorPosition( 0, 0, 1 );
607
608 printf("BIOS reported memory ranges:\n");
609 line = 1;
610 for (i=0; i<bootInfo->memoryMapCount; i++) {
611 printf("Base 0x%08x%08x, ",
612 (unsigned long)(mp->base >> 32),
613 (unsigned long)(mp->base));
614 printf("length 0x%08x%08x, type %d\n",
615 (unsigned long)(mp->length >> 32),
616 (unsigned long)(mp->length),
617 mp->type);
618 if (line++ > 20) {
619 printf("(Press a key to continue...)");
620 getc();
621 line = 0;
622 }
623 mp++;
624 }
625 if (line > 0) {
626 printf("(Press a key to continue...)");
627 getc();
628 }
629
630 setActiveDisplayPage(0);
631}
632
633char *getMemoryInfoString()
634{
635 int i;
636 MemoryRange *mp = bootInfo->memoryMap;
637char *buff = malloc(sizeof(char)*1024);
638if(!buff) return 0;
639
640char info[] = "BIOS reported memory ranges:\n";
641sprintf(buff, "%s", info);
642 for (i=0; i<bootInfo->memoryMapCount; i++) {
643 sprintf( buff+strlen(buff), "Base 0x%08x%08x, ",
644 (unsigned long)(mp->base >> 32),
645 (unsigned long)(mp->base));
646 sprintf( buff+strlen(buff), "length 0x%08x%08x, type %d\n",
647 (unsigned long)(mp->length >> 32),
648 (unsigned long)(mp->length),
649 mp->type);
650 mp++;
651 }
652return buff;
653}
654
655//==========================================================================
656extern void lspci(const char *booterParam);
657
658int
659getBootOptions(BOOL firstRun)
660{
661 int i;
662 int key;
663 int nextRow;
664 int timeout;
665 int bvCount;
666 BVRef bvr;
667 BVRef menuBVR;
668 BOOL showPrompt, newShowPrompt, isCDROM;
669
670 // Initialize default menu selection entry.
671gBootVolume = menuBVR = selectBootVolume(bvChain);
672
673if ( biosDevIsCDROM(gBIOSDev) )
674 isCDROM = TRUE;
675 else
676 isCDROM = FALSE;
677
678// ensure we're in graphics mode if gui is setup
679if(gui.initialised == YES)
680if( bootArgs->Video.v_display == VGA_TEXT_MODE )
681setVideoMode( GRAPHICS_MODE, 0 );
682
683 // Allow user to override default timeout.
684if (multiboot_timeout_set)
685timeout=multiboot_timeout;
686else if ( getIntForKey(kTimeoutKey, &timeout, &bootInfo->bootConfig) == NO )
687 {
688 /* If there is no timeout key in the file use the default timeout
689 which is different for CDs vs. hard disks. However, if not booting
690 a CD and no config file could be loaded set the timeout
691 to zero which causes the menu to display immediately.
692 This way, if no partitions can be found, that is the disk is unpartitioned
693 or simply cannot be read) then an empty menu is displayed.
694 If some partitions are found, for example a Windows partition, then
695 these will be displayed in the menu as foreign partitions.
696 */
697 if ( isCDROM )
698 timeout = kCDBootTimeout;
699 else
700 timeout = sysConfigValid?kBootTimeout:0;
701 }
702
703 if (timeout < 0)
704gBootMode |= kBootModeQuiet;
705
706 // If the user is holding down a modifier key,
707 // enter safe mode.
708 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) {
709 gBootMode |= kBootModeSafe;
710 }
711
712 // If user typed F8, abort quiet mode,
713 // and display the menu.
714{
715int f8press = FALSE, spress = FALSE, vpress = FALSE, key;
716 while ( readKeyboardStatus() ) {
717key = bgetc ();
718if (key == 0x4200) f8press = TRUE;
719if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = TRUE;
720if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = TRUE;
721}
722if (f8press)
723{
724gBootMode &= ~kBootModeQuiet;
725timeout = 0;
726}
727if ((gBootMode & kBootModeQuiet) && firstRun && vpress && (gBootArgsPtr + 3 < gBootArgsEnd))
728{
729*(gBootArgsPtr++) = ' ';
730*(gBootArgsPtr++) = '-';
731*(gBootArgsPtr++) = 'v';
732}
733if ((gBootMode & kBootModeQuiet) && firstRun && spress && (gBootArgsPtr + 3 < gBootArgsEnd))
734{
735*(gBootArgsPtr++) = ' ';
736*(gBootArgsPtr++) = '-';
737*(gBootArgsPtr++) = 's';
738}
739
740}
741
742clearBootArgs();
743
744if( bootArgs->Video.v_display == VGA_TEXT_MODE )
745{
746 setCursorPosition( 0, 0, 0 );
747 clearScreenRows( 0, kScreenLastRow );
748
749if ( ! ( gBootMode & kBootModeQuiet ) )
750{
751// Display banner and show hardware info.
752printf( bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024 );
753printf( getVBEInfoString() );
754}
755
756changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
757verbose("Scanning device %x...", gBIOSDev);
758
759}
760
761 // When booting from CD, default to hard
762 // drive boot when possible.
763
764 if ( isCDROM && firstRun )
765 {
766 const char *val;
767 char *prompt;
768char *name;
769 int cnt;
770 int optionKey;
771
772 if (getValueForKey( kCDROMPromptKey, &val, &cnt, &bootInfo->bootConfig )) {
773 cnt += 1;
774 prompt = malloc(cnt);
775 strlcpy(prompt, val, cnt);
776 } else {
777name = malloc(80);
778getBootVolumeDescription( gBootVolume, name, 80, NO );
779prompt = malloc(256);
780sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
781free(name);
782cnt = 0;
783}
784
785 if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->bootConfig ))
786{
787 // The key specified is a special key.
788 } else if (getValueForKey( kCDROMOptionKey, &val, &cnt, &bootInfo->bootConfig ) && cnt >= 1) {
789 optionKey = val[0];
790 } else {
791 // Default to F8.
792 optionKey = 0x4200;
793 }
794
795 // If the timeout is zero then it must have been set above due to the
796 // early catch of F8 which means the user wants to set boot options
797 // which we ought to interpret as meaning he wants to boot the CD.
798 if(timeout != 0)
799 key = countdown(prompt, kMenuTopRow, timeout);
800 else
801 key = optionKey;
802
803 if (cnt)
804 free(prompt);
805
806clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
807
808// Hit the option key ?
809if ( key == optionKey )
810{
811gBootMode &= ~kBootModeQuiet;
812timeout = 0;
813
814} else {
815
816key = key & 0xFF;
817
818// Try booting hard disk if user pressed 'h'
819if ( biosDevIsCDROM(gBIOSDev) && key == 'h' )
820{
821BVRef bvr;
822
823// Look at partitions hosting OS X other than the CD-ROM
824for ( bvr = bvChain; bvr; bvr = bvr->next )
825if ( (bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev )
826gBootVolume = bvr;
827}
828
829goto done;
830}
831}
832
833 if ( gBootMode & kBootModeQuiet )
834 {
835 // No input allowed from user.
836 goto done;
837 }
838
839 if ( firstRun && ( timeout > 0 ) &&
840 ( countdown("Press any key to enter startup options.",
841 kMenuTopRow, timeout) == 0 ) )
842 {
843 // If the user is holding down a modifier key,
844 // enter safe mode.
845 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 ) {
846 gBootMode |= kBootModeSafe;
847 }
848 goto done;
849 }
850
851 if ( gDeviceCount )
852 {
853 // Allocate memory for an array of menu items.
854 menuItems = (MenuItem *) malloc( sizeof(MenuItem) * gDeviceCount );
855 if ( menuItems == NULL ) goto done;
856
857// Associate a menu item for each BVRef.
858 for ( bvr = bvChain, i = gDeviceCount - 1, selectIndex = 0;
859 bvr; bvr = bvr->next)
860if (bvr->visible)
861{
862getBootVolumeDescription( bvr, menuItems[i].name, 80, YES );
863menuItems[i].param = (void *) bvr;
864if ( bvr == menuBVR ) selectIndex = i;
865i--;
866}
867
868}
869
870if( bootArgs->Video.v_display == GRAPHICS_MODE )
871{
872// redraw the background buffer
873drawBackground();
874gui.devicelist.draw = YES;
875gui.redraw = YES;
876if ( ! ( gBootMode & kBootModeQuiet ) )
877
878{
879 BOOL showBootBanner = YES;
880
881 // Check if "Boot Banner"=N switch is present in config file.
882 getBoolForKey( kBootBannerKey, &showBootBanner, &bootInfo->bootConfig);
883
884 if ( showBootBanner )
885 {
886 // Display banner and show hardware info.
887 gprintf( &gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024 );
888 }
889
890// redraw background
891memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
892}
893} else {
894 // Clear screen and hide the blinking cursor.
895 clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
896 changeCursor( 0, kMenuTopRow, kCursorTypeHidden, 0 );
897 }
898
899 nextRow = kMenuTopRow;
900 showPrompt = YES;
901
902 if ( gDeviceCount )
903 {
904 if( bootArgs->Video.v_display == VGA_TEXT_MODE )
905 printf("Use \30\31 keys to select the startup volume.");
906
907 showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
908 nextRow += min( gDeviceCount, kMenuMaxItems ) + 3;
909 }
910
911// Show the boot prompt.
912showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
913showBootPrompt( nextRow, showPrompt );
914
915 do {
916
917 if( bootArgs->Video.v_display == GRAPHICS_MODE )
918 {
919 // redraw background
920 memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
921
922 // reset cursor co-ords
923 gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
924
925 }
926
927 key = getc();
928
929 updateMenu( key, (void **) &menuBVR );
930
931 newShowPrompt = (gDeviceCount == 0) ||
932 (menuBVR->flags & kBVFlagNativeBoot);
933
934 if ( newShowPrompt != showPrompt )
935 {
936 showPrompt = newShowPrompt;
937 showBootPrompt( nextRow, showPrompt );
938 }
939
940 if ( showPrompt )
941 updateBootArgs( key );
942
943 switch ( key )
944 {
945 case kReturnKey:
946
947 if ( gui.menu.draw )
948 {
949 key=0;
950 break;
951 }
952
953 if ( *gBootArgs == '?' )
954 {
955 char * argPtr = gBootArgs;
956
957 // Skip the leading "?" character.
958 argPtr++;
959 getNextArg(&argPtr, booterCommand);
960 getNextArg(&argPtr, booterParam);
961
962 /*
963 * TODO: this needs to be refactored.
964 */
965
966 if ( strcmp( booterCommand, "video" ) == 0 )
967 {
968 if( bootArgs->Video.v_display == GRAPHICS_MODE )
969 showInfoBox( getVBEInfoString(), getVBEModeInfoString() );
970 else
971 printVBEModeInfo();
972 }
973
974 else if ( strcmp( booterCommand, "memory" ) == 0 )
975 if( bootArgs->Video.v_display == GRAPHICS_MODE )
976 showInfoBox( "Memory Map", getMemoryInfoString() );
977 else
978 printMemoryInfo();
979
980 else if ( strcmp( booterCommand, "lspci" ) == 0 )
981 lspci(booterParam);
982
983 else if ( strcmp( booterCommand, "more" ) == 0 )
984 showTextFile(booterParam);
985
986 else if ( strcmp( booterCommand, "rd" ) == 0 )
987 processRAMDiskCommand(&argPtr, booterParam);
988
989 else if ( strcmp( booterCommand, "norescan" ) == 0 )
990 {
991 if (gEnableCDROMRescan)
992 {
993 gEnableCDROMRescan = FALSE;
994 break;
995 }
996 }
997
998 else
999 {
1000 showHelp();
1001 }
1002
1003 key = 0;
1004 showBootPrompt( nextRow, showPrompt );
1005 break;
1006 }
1007
1008 gBootVolume = menuBVR;
1009 setRootVolume(menuBVR);
1010 gBIOSDev = menuBVR->biosdev;
1011 break;
1012
1013 case kEscapeKey:
1014 clearBootArgs();
1015 break;
1016
1017 case kF5Key:
1018 // New behavior:
1019 // Clear gBootVolume to restart the loop
1020 // if the user enabled rescanning the optical drive.
1021 // Otherwise boot the default boot volume.
1022 if (gEnableCDROMRescan)
1023 {
1024 gBootVolume = NULL;
1025 clearBootArgs();
1026 }
1027 break;
1028
1029 case kF10Key:
1030 gScanSingleDrive = FALSE;
1031 scanDisks(gBIOSDev, &bvCount);
1032 gBootVolume = NULL;
1033 clearBootArgs();
1034 break;
1035
1036 case kTabKey:
1037 // New behavior:
1038 // Switch between text & graphic interfaces
1039 // Only Permitted if started in graphics interface
1040 if (useGUI)
1041 {
1042 if (bootArgs->Video.v_display == GRAPHICS_MODE)
1043 {
1044 setVideoMode( VGA_TEXT_MODE, 0 );
1045
1046 setCursorPosition( 0, 0, 0 );
1047 clearScreenRows( 0, kScreenLastRow );
1048
1049 // Display banner and show hardware info.
1050 printf( bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024 );
1051 printf( getVBEInfoString() );
1052
1053 clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
1054 changeCursor( 0, kMenuTopRow, kCursorTypeHidden, 0 );
1055
1056 nextRow = kMenuTopRow;
1057 showPrompt = YES;
1058
1059 if ( gDeviceCount )
1060 {
1061 printf("Use \30\31 keys to select the startup volume.");
1062 showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
1063 nextRow += min( gDeviceCount, kMenuMaxItems ) + 3;
1064 }
1065
1066 showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1067 showBootPrompt( nextRow, showPrompt );
1068
1069 //changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1070
1071 }
1072 else
1073 {
1074 gui.redraw = YES;
1075 setVideoMode( GRAPHICS_MODE, 0 );
1076 updateVRAM();
1077 }
1078 }
1079 key = 0;
1080 break;
1081
1082 default:
1083 key = 0;
1084 }
1085 }
1086 while ( 0 == key );
1087
1088done:
1089 firstRun = NO;
1090
1091if( bootArgs->Video.v_display == VGA_TEXT_MODE )
1092 {
1093clearScreenRows( kMenuTopRow, kScreenLastRow );
1094changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1095 }
1096
1097 shouldboot = NO;
1098gui.menu.draw = NO;
1099
1100 if ( menuItems )
1101free(menuItems);
1102
1103 return 0;
1104}
1105
1106//==========================================================================
1107
1108extern unsigned char chainbootdev;
1109extern unsigned char chainbootflag;
1110
1111BOOL
1112copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1113{
1114 int argLen = argName ? strlen(argName) : 0;
1115 int len = argLen + cnt + 1; // +1 to account for space
1116
1117 if (len > *cntRemainingP) {
1118 error("Warning: boot arguments too long, truncating\n");
1119 return NO;
1120 }
1121
1122 if (argName) {
1123 strncpy( *argP, argName, argLen );
1124 *argP += argLen;
1125 *argP[0] = '=';
1126 (*argP)++;
1127 len++; // +1 to account for '='
1128 }
1129 strncpy( *argP, val, cnt );
1130 *argP += cnt;
1131 *argP[0] = ' ';
1132 (*argP)++;
1133
1134 *cntRemainingP -= len;
1135 return YES;
1136}
1137
1138//
1139// Returns TRUE if an argument was copied, FALSE otherwise
1140
1141BOOL
1142processBootArgument(
1143 const char *argName, // The argument to search for
1144 const char *userString, // Typed-in boot arguments
1145 const char *kernelFlags, // Kernel flags from config table
1146 const char *configTable,
1147 char **argP, // Output value
1148 int *cntRemainingP, // Output count
1149 char *foundVal // found value
1150 )
1151{
1152 const char *val;
1153 int cnt;
1154 BOOL found = NO;
1155
1156 if (getValueForBootKey(userString, argName, &val, &cnt)) {
1157 // Don't copy; these values will be copied at the end of argument processing.
1158 found = YES;
1159 } else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1160 // Don't copy; these values will be copied at the end of argument processing.
1161 found = YES;
1162 } else if (getValueForKey(argName, &val, &cnt, &bootInfo->bootConfig)) {
1163 copyArgument(argName, val, cnt, argP, cntRemainingP);
1164 found = YES;
1165 }
1166 if (found && foundVal) {
1167 strlcpy(foundVal, val, cnt+1);
1168 }
1169 return found;
1170}
1171
1172// Maximum config table value size
1173#define VALUE_SIZE 2048
1174
1175int
1176processBootOptions()
1177{
1178 const char * cp = gBootArgs;
1179 const char * val = 0;
1180 const char * kernel;
1181 int cnt;
1182 int userCnt;
1183 int cntRemaining;
1184 char * argP;
1185 char uuidStr[64];
1186 BOOL uuidSet = NO;
1187 char * configKernelFlags;
1188 char * valueBuffer;
1189
1190 valueBuffer = (char *)malloc(VALUE_SIZE);
1191
1192 skipblanks( &cp );
1193
1194 // Update the unit and partition number.
1195
1196 if ( gBootVolume )
1197 {
1198 if (!( gBootVolume->flags & kBVFlagNativeBoot ))
1199 {
1200 readBootSector( gBootVolume->biosdev, gBootVolume->part_boff,
1201 (void *) 0x7c00 );
1202
1203 //
1204 // Setup edx, and signal intention to chain load the
1205 // foreign booter.
1206 //
1207
1208 chainbootdev = gBootVolume->biosdev;
1209 chainbootflag = 1;
1210
1211 return 1;
1212 }
1213
1214 setRootVolume(gBootVolume);
1215
1216 }
1217 // If no boot volume fail immediately because we're just going to fail
1218 // trying to load the config file anyway.
1219 else
1220 return -1;
1221
1222 // Load config table specified by the user, or use the default.
1223
1224 if (getValueForBootKey( cp, "config", &val, &cnt ) == FALSE) {
1225 val = 0;
1226 cnt = 0;
1227 }
1228
1229 // Load com.apple.Boot.plist from the selected volume
1230 // and use its contents to override default bootConfig.
1231 // This is not a mandatory opeartion anymore.
1232
1233 loadOverrideConfig(&bootInfo->overrideConfig);
1234
1235 // Use the kernel name specified by the user, or fetch the name
1236 // in the config table, or use the default if not specified.
1237 // Specifying a kernel name on the command line, or specifying
1238 // a non-default kernel name in the config file counts as
1239 // overriding the kernel, which causes the kernelcache not
1240 // to be used.
1241
1242 gOverrideKernel = NO;
1243 if (( kernel = extractKernelName((char **)&cp) )) {
1244 strcpy( bootInfo->bootFile, kernel );
1245 gOverrideKernel = YES;
1246 } else {
1247 if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) ) {
1248 strlcpy( bootInfo->bootFile, val, cnt+1 );
1249 if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) {
1250 gOverrideKernel = YES;
1251 }
1252 } else {
1253 strcpy( bootInfo->bootFile, kDefaultKernel );
1254 }
1255 }
1256
1257 cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1258 argP = bootArgs->CommandLine;
1259
1260 // Get config table kernel flags, if not ignored.
1261 if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) == TRUE ||
1262 getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig ) == FALSE) {
1263 val = "";
1264 cnt = 0;
1265 }
1266 configKernelFlags = (char *)malloc(cnt + 1);
1267 strlcpy(configKernelFlags, val, cnt + 1);
1268
1269 if (processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, 0)) {
1270 // boot-uuid was set either on the command-line
1271 // or in the config file.
1272 uuidSet = YES;
1273 } else {
1274
1275 //
1276 // Try an alternate method for getting the root UUID on boot helper partitions.
1277 //
1278 if (gBootVolume->flags & kBVFlagBooter)
1279 {
1280 if((loadHelperConfig(&bootInfo->helperConfig) == 0)
1281 && getValueForKey(kHelperRootUUIDKey, &val, &cnt, &bootInfo->helperConfig) )
1282 {
1283 getValueForKey(kHelperRootUUIDKey, &val, &cnt, &bootInfo->helperConfig);
1284 copyArgument(kBootUUIDKey, val, cnt, &argP, &cntRemaining);
1285 uuidSet = YES;
1286 }
1287 }
1288
1289 if (!uuidSet && gBootVolume->fs_getuuid && gBootVolume->fs_getuuid (gBootVolume, uuidStr) == 0) {
1290 verbose("Setting boot-uuid to: %s\n", uuidStr);
1291 copyArgument(kBootUUIDKey, uuidStr, strlen(uuidStr), &argP, &cntRemaining);
1292 uuidSet = YES;
1293 }
1294 }
1295
1296 if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gRootDevice)) {
1297 cnt = 0;
1298 if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->bootConfig)) {
1299 valueBuffer[0] = '*';
1300 cnt++;
1301 strlcpy(valueBuffer + 1, val, cnt);
1302 val = valueBuffer;
1303 } else {
1304 if (uuidSet) {
1305 val = "*uuid";
1306 cnt = 5;
1307 } else {
1308 // Don't set "rd=.." if there is no boot device key
1309 // and no UUID.
1310 val = "";
1311 cnt = 0;
1312 }
1313 }
1314 if (cnt > 0) {
1315 copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1316 }
1317 strlcpy( gRootDevice, val, (cnt + 1));
1318 }
1319
1320 /*
1321 * Removed. We don't need this anymore.
1322 *
1323 if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gPlatformName)) {
1324 getPlatformName(gPlatformName);
1325 copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1326 }
1327 */
1328
1329 if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1330 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt)) {
1331 if (gBootMode & kBootModeSafe) {
1332 copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1333 }
1334 }
1335
1336 // Store the merged kernel flags and boot args.
1337
1338 cnt = strlen(configKernelFlags);
1339 if (cnt) {
1340 if (cnt > cntRemaining) {
1341 error("Warning: boot arguments too long, truncating\n");
1342 cnt = cntRemaining;
1343 }
1344 strncpy(argP, configKernelFlags, cnt);
1345 argP[cnt++] = ' ';
1346 cntRemaining -= cnt;
1347 }
1348 userCnt = strlen(cp);
1349 if (userCnt > cntRemaining) {
1350 error("Warning: boot arguments too long, truncating\n");
1351 userCnt = cntRemaining;
1352 }
1353 strncpy(&argP[cnt], cp, userCnt);
1354 argP[cnt+userCnt] = '\0';
1355
1356 if(!shouldboot)
1357 {
1358 gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->bootConfig ) ||
1359 getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->bootConfig );
1360
1361 gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->bootConfig ) ) ?
1362 kBootModeSafe : kBootModeNormal;
1363
1364 if ( getValueForKey( kOldSafeModeFlag, &val, &cnt, &bootInfo->bootConfig ) ) {
1365 gBootMode = kBootModeSafe;
1366 }
1367
1368 if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) ) {
1369 strlcpy(gMKextName, val, cnt + 1);
1370 }
1371
1372 }
1373
1374 free(configKernelFlags);
1375 free(valueBuffer);
1376
1377 return 0;
1378}
1379
1380
1381//==========================================================================
1382// Load the help file and display the file contents on the screen.
1383
1384void showHelp()
1385{
1386#define BOOT_HELP_PATH "bt(0,0)/Extra/BootHelp.txt"
1387
1388#ifdef EMBED_THEME
1389if( bootArgs->Video.v_display == GRAPHICS_MODE )
1390{
1391showInfoBox( "Help. Press q to quit.\n" , (char *)BootHelp_txt );
1392return;
1393}
1394#endif
1395 showTextFile(BOOT_HELP_PATH);
1396}
1397
1398void showTextFile(const char * filename)
1399{
1400#define MAX_TEXT_FILE_SIZE 65536
1401
1402 int fd;
1403 int size;
1404 int line;
1405 int line_offset;
1406 int c;
1407 char fn[1024];
1408
1409 *fn = '\0';
1410 strcat(fn, filename);
1411
1412 if ( (fd = open(fn, 0)) >= 0 )
1413 {
1414 char * buffer;
1415 char * bp;
1416
1417 size = file_size(fd);
1418 if (size > MAX_TEXT_FILE_SIZE)
1419size = MAX_TEXT_FILE_SIZE;
1420 buffer = malloc( size + 1 );
1421 read(fd, buffer, size);
1422 close(fd);
1423
1424if( bootArgs->Video.v_display == GRAPHICS_MODE )
1425{
1426showInfoBox( "Help. Press q to quit.\n" , buffer );
1427return;
1428}
1429
1430 bp = buffer;
1431 while (size > 0) {
1432 while (*bp != '\n') {
1433 bp++;
1434 size--;
1435 }
1436 *bp++ = '\0';
1437 size--;
1438 }
1439 *bp = '\1';
1440 line_offset = 0;
1441
1442 setActiveDisplayPage(1);
1443
1444 while (1) {
1445 clearScreenRows(0, 24);
1446 setCursorPosition(0, 0, 1);
1447 bp = buffer;
1448 for (line = 0; *bp != '\1' && line < line_offset; line++) {
1449 while (*bp != '\0') bp++;
1450 bp++;
1451 }
1452 for (line = 0; *bp != '\1' && line < 23; line++) {
1453 setCursorPosition(0, line, 1);
1454 printf("%s\n", bp);
1455 while (*bp != '\0') bp++;
1456 bp++;
1457 }
1458
1459 setCursorPosition(0, 23, 1);
1460 if (*bp == '\1') {
1461 printf("[Type %sq or space to quit viewer]",
1462 (line_offset > 0) ? "p for previous page, " : "");
1463 } else {
1464 printf("[Type %s%sq to quit viewer]",
1465 (line_offset > 0) ? "p for previous page, " : "",
1466 (*bp != '\1') ? "space for next page, " : "");
1467 }
1468
1469 c = getc();
1470 if (c == 'q' || c == 'Q') {
1471 break;
1472 }
1473 if ((c == 'p' || c == 'P') && line_offset > 0) {
1474 line_offset -= 23;
1475 }
1476 if (c == ' ') {
1477 if (*bp == '\1') {
1478 break;
1479 } else {
1480 line_offset += 23;
1481 }
1482 }
1483 }
1484
1485 free(buffer);
1486 setActiveDisplayPage(0);
1487 }
1488 else
1489 {
1490 printf("\nFile not found: %s\n", fn);
1491 sleep(2);
1492 }
1493}
1494
1495static inline int isHexDigit(char ch)
1496{
1497 return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f');
1498}
1499
1500// This is a very simplistic prompting scheme that just grabs two hex characters
1501// Eventually we need to do something more user-friendly like display a menu
1502// based off of the Multiboot device list
1503
1504int selectAlternateBootDevice(int bootdevice)
1505{
1506 int digitsI = 0;
1507 char digits[3] = {0,0,0};
1508
1509 // We've already printed the current boot device so user knows what it is
1510 printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1511 printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1512 int key = 0;
1513 do {
1514 key = getc();
1515 switch(key & kASCIIKeyMask)
1516 {
1517 case kBackspaceKey:
1518 if(digitsI > 0)
1519 {
1520 int x, y, t;
1521 getCursorPositionAndType(&x, &y, &t);
1522 // Assume x is not 0;
1523 x--;
1524 setCursorPosition(x,y,0); // back up one char
1525 // Overwrite with space without moving cursor position
1526 putca(' ', 0x07, 1);
1527 digitsI--;
1528 }
1529 else
1530 {
1531 // TODO: Beep or something
1532 }
1533 break;
1534 case kReturnKey:
1535 if(1)
1536 {
1537 digits[digitsI] = '\0';
1538 char *end;
1539 int newbootdevice = strtol(digits, &end, 16);
1540 if(end == digits && *end == '\0') // User entered empty string
1541 {
1542 printf("\nUsing default boot device %x\n", bootdevice);
1543 key = 0;
1544 }
1545 else if(end != digits && *end == '\0')
1546 {
1547 bootdevice = newbootdevice;
1548 printf("\n");
1549 key = 0; // We gots da boot device
1550 }
1551 else
1552 {
1553 printf("\nCouldn't parse. try again: ");
1554 digitsI = 0;
1555 }
1556 }
1557 break;
1558 default:
1559 if( isHexDigit(key & kASCIIKeyMask) && digitsI < 2 )
1560 {
1561 putc(key & kASCIIKeyMask);
1562 digits[digitsI++] = key & kASCIIKeyMask;
1563 }
1564 else
1565 {
1566 // TODO: Beep or something
1567 }
1568 };
1569 } while(key != 0);
1570 return bootdevice;
1571}
1572
1573
1574BOOL promptForRescanOption(void)
1575{
1576 int key;
1577 BOOL result = FALSE;
1578
1579 printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1580 key = getc();
1581
1582 switch ( key )
1583 {
1584 case kReturnKey:
1585 result = TRUE;
1586 break;
1587 }
1588
1589 return result;
1590}

Archive Download this file

Revision: 1