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)
1017{
1018if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1019{
1020setVideoMode(VGA_TEXT_MODE, 0);
1021
1022setCursorPosition(0, 0, 0);
1023clearScreenRows(0, kScreenLastRow);
1024
1025// Display banner and show hardware info.
1026printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1027printf(getVBEInfoString());
1028
1029clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1030changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1031
1032nextRow = kMenuTopRow;
1033showPrompt = true;
1034
1035if (gDeviceCount)
1036{
1037printf("Use \30\31 keys to select the startup volume.");
1038showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1039nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1040}
1041
1042showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1043showBootPrompt(nextRow, showPrompt);
1044//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1045} else {
1046gui.redraw = true;
1047setVideoMode(GRAPHICS_MODE, 0);
1048updateVRAM();
1049 updateGraphicBootPrompt();
1050}
1051}
1052key = 0;
1053break;
1054
1055default:
1056key = 0;
1057break;
1058}
1059} while (0 == key);
1060
1061done:
1062if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1063clearScreenRows(kMenuTopRow, kScreenLastRow);
1064changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1065}
1066shouldboot = false;
1067gui.menu.draw = false;
1068if (menuItems) {
1069free(menuItems);
1070menuItems = NULL;
1071}
1072return 0;
1073}
1074
1075//==========================================================================
1076
1077char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1078extern unsigned char chainbootdev;
1079extern unsigned char chainbootflag;
1080
1081bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1082{
1083int argLen = argName ? strlen(argName) : 0;
1084int len = argLen + cnt + 1; // + 1 to account for space.
1085
1086if (len > *cntRemainingP)
1087{
1088error("Warning: boot arguments too long, truncating\n");
1089return false;
1090}
1091
1092if (argName)
1093{
1094strncpy(*argP, argName, argLen);
1095*argP += argLen;
1096*argP[0] = '=';
1097(*argP)++;
1098len++; // +1 to account for '='
1099}
1100
1101strncpy(*argP, val, cnt);
1102*argP += cnt;
1103*argP[0] = ' ';
1104(*argP)++;
1105*cntRemainingP -= len;
1106
1107return true;
1108}
1109
1110//
1111// Returns TRUE if an argument was copied, FALSE otherwise
1112bool
1113processBootArgument(
1114 const char *argName, // The argument to search for
1115 const char *userString, // Typed-in boot arguments
1116 const char *kernelFlags, // Kernel flags from config table
1117 const char *configTable,
1118 char **argP, // Output value
1119 int *cntRemainingP, // Output count
1120 char *foundVal, // found value
1121 int foundValSize // max found value size
1122 )
1123{
1124 const char *val;
1125 int cnt;
1126 bool found = false;
1127
1128if (getValueForBootKey(userString, argName, &val, &cnt))
1129{
1130// Don't copy; these values will be copied at the end of argument processing.
1131found = true;
1132}
1133else if (getValueForBootKey(kernelFlags, argName, &val, &cnt))
1134{
1135// Don't copy; these values will be copied at the end of argument processing.
1136found = true;
1137}
1138else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig))
1139{
1140copyArgument(argName, val, cnt, argP, cntRemainingP);
1141found = true;
1142}
1143if (found && foundVal)
1144{
1145strlcpy(foundVal, val, foundValSize);
1146}
1147return found;
1148}
1149
1150// Maximum config table value size
1151#define VALUE_SIZE 2048
1152
1153int processBootOptions()
1154{
1155const char *cp = gBootArgs;
1156const char *val = 0;
1157const char *kernel;
1158int cnt;
1159int userCnt;
1160int cntRemaining;
1161char *argP;
1162char *configKernelFlags;
1163char *valueBuffer;
1164
1165valueBuffer = malloc(VALUE_SIZE);
1166
1167skipblanks( &cp );
1168
1169// Update the unit and partition number.
1170
1171if ( gBootVolume )
1172{
1173if (!( gBootVolume->flags & kBVFlagNativeBoot ))
1174{
1175readBootSector( gBootVolume->biosdev, gBootVolume->part_boff, (void *) 0x7c00 );
1176//
1177// Setup edx, and signal intention to chain load the
1178// foreign booter.
1179//
1180
1181chainbootdev = gBootVolume->biosdev;
1182chainbootflag = 1;
1183
1184return 1;
1185}
1186
1187setRootVolume(gBootVolume);
1188
1189}
1190 // If no boot volume fail immediately because we're just going to fail
1191 // trying to load the config file anyway.
1192 else
1193 return -1;
1194
1195 // Load config table specified by the user, or use the default.
1196
1197 if (!getValueForBootKey(cp, "config", &val, &cnt)) {
1198 val = 0;
1199 cnt = 0;
1200 }
1201
1202 // Load com.apple.Boot.plist from the selected volume
1203 // and use its contents to override default bootConfig.
1204
1205 loadSystemConfig(&bootInfo->bootConfig);
1206 loadChameleonConfig(&bootInfo->chameleonConfig);
1207
1208 // Use the kernel name specified by the user, or fetch the name
1209 // in the config table, or use the default if not specified.
1210 // Specifying a kernel name on the command line, or specifying
1211 // a non-default kernel name in the config file counts as
1212 // overriding the kernel, which causes the kernelcache not
1213 // to be used.
1214
1215 gOverrideKernel = false;
1216 if (( kernel = extractKernelName((char **)&cp) )) {
1217 strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1218 } else {
1219 if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) ) {
1220 strlcpy( bootInfo->bootFile, val, cnt+1 );
1221 } else {
1222 strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1223 }
1224 }
1225if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) {
1226gOverrideKernel = true;
1227}
1228
1229 cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1230 argP = bootArgs->CommandLine;
1231
1232// Get config kernel flags, if not ignored.
1233 if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1234 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig )) {
1235 val = "";
1236 cnt = 0;
1237 }
1238 configKernelFlags = malloc(cnt + 1);
1239 strlcpy(configKernelFlags, val, cnt + 1);
1240
1241 // boot-uuid can be set either on the command-line or in the config file
1242if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1243 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString))) {
1244 //
1245 // Try an alternate method for getting the root UUID on boot helper partitions.
1246 //
1247 if (gBootVolume->flags & kBVFlagBooter)
1248{
1249// Load the configuration store in the boot helper partition
1250if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1251 {
1252val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1253if (val != NULL)
1254strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1255}
1256 }
1257
1258 // Try to get the volume uuid string
1259if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1260gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1261
1262// If we have the volume uuid add it to the commandline arguments
1263if (strlen(gBootUUIDString))
1264copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1265}
1266
1267 if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1268 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE)) {
1269 cnt = 0;
1270 if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig)) {
1271 valueBuffer[0] = '*';
1272 cnt++;
1273 strlcpy(valueBuffer + 1, val, cnt);
1274 val = valueBuffer;
1275 } else {
1276 if (strlen(gBootUUIDString)) {
1277 val = "*uuid";
1278 cnt = 5;
1279 } else {
1280 // Don't set "rd=.." if there is no boot device key
1281 // and no UUID.
1282 val = "";
1283 cnt = 0;
1284 }
1285 }
1286 if (cnt > 0) {
1287 copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1288 }
1289 strlcpy( gRootDevice, val, (cnt + 1));
1290 }
1291
1292 /*
1293 * Removed. We don't need this anymore.
1294 *
1295 if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1296 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler))) {
1297 getPlatformName(gPlatformName);
1298 copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1299 }
1300 */
1301
1302 if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1303 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt)) {
1304 if (gBootMode & kBootModeSafe) {
1305 copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1306 }
1307 }
1308
1309 // Store the merged kernel flags and boot args.
1310
1311 cnt = strlen(configKernelFlags);
1312 if (cnt) {
1313 if (cnt > cntRemaining) {
1314 error("Warning: boot arguments too long, truncating\n");
1315 cnt = cntRemaining;
1316 }
1317 strncpy(argP, configKernelFlags, cnt);
1318 argP[cnt++] = ' ';
1319 cntRemaining -= cnt;
1320 }
1321 userCnt = strlen(cp);
1322 if (userCnt > cntRemaining) {
1323 error("Warning: boot arguments too long, truncating\n");
1324 userCnt = cntRemaining;
1325 }
1326 strncpy(&argP[cnt], cp, userCnt);
1327 argP[cnt+userCnt] = '\0';
1328
1329if(!shouldboot)
1330{
1331gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1332getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1333
1334gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1335kBootModeSafe : kBootModeNormal;
1336
1337 if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) {
1338 gBootMode = kBootModeSafe;
1339 }
1340}
1341
1342if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1343strlcpy(gMKextName, val, cnt + 1);
1344else
1345gMKextName[0]=0;
1346
1347 free(configKernelFlags);
1348 free(valueBuffer);
1349
1350 return 0;
1351}
1352
1353
1354//==========================================================================
1355// Load the help file and display the file contents on the screen.
1356
1357void showTextBuffer(char *buf_orig, int size)
1358{
1359char*bp;
1360char* buf;
1361intline;
1362intline_offset;
1363intc;
1364
1365if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1366{
1367showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1368return;
1369}
1370
1371// Create a copy so that we don't mangle the original
1372buf = malloc(size + 1);
1373memcpy(buf, buf_orig, size);
1374
1375
1376 bp = buf;
1377 while (size-- > 0)
1378{
1379if (*bp == '\n')
1380{
1381*bp = '\0';
1382}
1383bp++;
1384 }
1385 *bp = '\1';
1386 line_offset = 0;
1387
1388 setActiveDisplayPage(1);
1389
1390 while (1)
1391{
1392clearScreenRows(0, 24);
1393setCursorPosition(0, 0, 1);
1394bp = buf;
1395for (line = 0; *bp != '\1' && line < line_offset; line++)
1396{
1397while (*bp != '\0')
1398{
1399bp++;
1400}
1401bp++;
1402}
1403for (line = 0; *bp != '\1' && line < 23; line++)
1404{
1405setCursorPosition(0, line, 1);
1406printf("%s\n", bp);
1407while (*bp != '\0')
1408{
1409bp++;
1410}
1411bp++;
1412}
1413
1414setCursorPosition(0, 23, 1);
1415if (*bp == '\1')
1416{
1417printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1418}
1419else
1420{
1421printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1422}
1423
1424c = getchar();
1425if (c == 'q' || c == 'Q')
1426{
1427break;
1428}
1429if ((c == 'p' || c == 'P') && line_offset > 0)
1430{
1431line_offset -= 23;
1432}
1433if (c == ' ')
1434{
1435if (*bp == '\1')
1436{
1437break;
1438}
1439else
1440{
1441line_offset += 23;
1442}
1443}
1444 }
1445 setActiveDisplayPage(0);
1446}
1447
1448void showHelp(void)
1449{
1450if (bootArgs->Video.v_display != VGA_TEXT_MODE)
1451{
1452showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1453}
1454else
1455{
1456showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1457}
1458}
1459
1460void showTextFile(const char * filename)
1461{
1462#define MAX_TEXT_FILE_SIZE 65536
1463char*buf;
1464intfd;
1465intsize;
1466
1467if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0)
1468{
1469printf("\nFile not found: %s\n", filename);
1470sleep(2);
1471return;
1472}
1473
1474 size = file_size(fd);
1475 if (size > MAX_TEXT_FILE_SIZE)
1476{
1477size = MAX_TEXT_FILE_SIZE;
1478}
1479 buf = malloc(size);
1480 read(fd, buf, size);
1481 close(fd);
1482showTextBuffer(buf, size);
1483free(buf);
1484}
1485
1486// This is a very simplistic prompting scheme that just grabs two hex characters
1487// Eventually we need to do something more user-friendly like display a menu
1488// based off of the Multiboot device list
1489
1490int selectAlternateBootDevice(int bootdevice)
1491{
1492int key;
1493int newbootdevice;
1494int digitsI = 0;
1495char *end;
1496char digits[3] = {0,0,0};
1497
1498// We've already printed the current boot device so user knows what it is
1499printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1500printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1501do {
1502key = getchar();
1503switch (ASCII_KEY(key))
1504{
1505case KEY_BKSP:
1506if (digitsI > 0)
1507{
1508int x, y, t;
1509getCursorPositionAndType(&x, &y, &t);
1510// Assume x is not 0;
1511x--;
1512setCursorPosition(x,y,0); // back up one char
1513// Overwrite with space without moving cursor position
1514putca(' ', 0x07, 1);
1515digitsI--;
1516}
1517else
1518{
1519// TODO: Beep or something
1520}
1521break;
1522
1523case KEY_ENTER:
1524digits[digitsI] = '\0';
1525newbootdevice = strtol(digits, &end, 16);
1526if (end == digits && *end == '\0')
1527{
1528// User entered empty string
1529printf("\nUsing default boot device %x\n", bootdevice);
1530key = 0;
1531}
1532else if(end != digits && *end == '\0')
1533{
1534bootdevice = newbootdevice;
1535printf("\n");
1536key = 0; // We gots da boot device
1537}
1538else
1539{
1540printf("\nCouldn't parse. try again: ");
1541digitsI = 0;
1542}
1543break;
1544
1545default:
1546if (isxdigit(ASCII_KEY(key)) && digitsI < 2)
1547{
1548putchar(ASCII_KEY(key));
1549digits[digitsI++] = ASCII_KEY(key);
1550}
1551else
1552{
1553// TODO: Beep or something
1554}
1555break;
1556};
1557} while (key != 0);
1558
1559return bootdevice;
1560}
1561
1562bool promptForRescanOption(void)
1563{
1564printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1565if (getchar() == KEY_ENTER)
1566{
1567return true;
1568}
1569else
1570{
1571return false;
1572}
1573}
1574

Archive Download this file

Revision: 2119