Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2167