Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2329