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

Archive Download this file

Revision: 2249