Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 1691