Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Trunk/i386/boot2/options.c

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

Archive Download this file

Revision: 2060