Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2323