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

Archive Download this file

Revision: 2166