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{
644 int i, bufflen;
645 MemoryRange *mp = bootInfo->memoryMap;
646 char *buff = malloc(sizeof(char)*1024);
647 if (!buff)
648 return 0;
649
650 static const char info[] = "BIOS reported memory ranges:\n";
651 bufflen = sprintf(buff, "%s", info);
652
653 for (i = 0;
654 (i < bootInfo->memoryMapCount) && (bufflen < 1024); /* prevent buffer overflow */
655 i++) {
656 bufflen += snprintf(buff+bufflen, 1024-bufflen, "Base 0x%08x%08x, ",
657 (unsigned long)(mp->base >> 32),
658 (unsigned long)(mp->base));
659 bufflen += snprintf(buff+bufflen, 1024-bufflen, "length 0x%08x%08x, type %d\n",
660 (unsigned long)(mp->length >> 32),
661 (unsigned long)(mp->length),
662 mp->type);
663 mp++;
664 }
665 return buff;
666}
667
668//==========================================================================
669
670void lspci(void)
671{
672if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
673setActiveDisplayPage(1);
674clearScreenRows(0, 24);
675setCursorPosition(0, 0, 1);
676}
677
678dump_pci_dt(root_pci_dev->children);
679
680pause();
681
682if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
683setActiveDisplayPage(0);
684}
685}
686
687//==========================================================================
688
689int getBootOptions(bool firstRun)
690{
691int i;
692int key;
693int nextRow;
694int timeout;
695int bvCount;
696BVRef bvr;
697BVRef menuBVR;
698bool showPrompt, newShowPrompt, isCDROM;
699
700// Initialize default menu selection entry.
701gBootVolume = menuBVR = selectBootVolume(bvChain);
702
703if (biosDevIsCDROM(gBIOSDev)) {
704isCDROM = true;
705} else {
706isCDROM = false;
707}
708
709// ensure we're in graphics mode if gui is setup
710if (firstRun && gui.initialised && bootArgs->Video.v_display == VGA_TEXT_MODE)
711{
712setVideoMode(GRAPHICS_MODE, 0);
713}
714
715// Clear command line boot arguments
716clearBootArgs();
717
718// Allow user to override default timeout.
719if (multiboot_timeout_set) {
720timeout = multiboot_timeout;
721} else if (!getIntForKey(kTimeoutKey, &timeout, &bootInfo->chameleonConfig)) {
722/* If there is no timeout key in the file use the default timeout
723 which is different for CDs vs. hard disks. However, if not booting
724 a CD and no config file could be loaded set the timeout
725 to zero which causes the menu to display immediately.
726 This way, if no partitions can be found, that is the disk is unpartitioned
727 or simply cannot be read) then an empty menu is displayed.
728 If some partitions are found, for example a Windows partition, then
729 these will be displayed in the menu as foreign partitions.
730 */
731if (isCDROM) {
732timeout = kCDBootTimeout;
733} else {
734timeout = sysConfigValid ? kBootTimeout : 0;
735}
736}
737
738if (timeout < 0) {
739gBootMode |= kBootModeQuiet;
740}
741
742// If the user is holding down a modifier key, enter safe mode.
743if ((readKeyboardShiftFlags() & 0x0F) != 0) {
744gBootMode |= kBootModeSafe;
745}
746
747// Checking user pressed keys
748bool f8press = false, spress = false, vpress = false;
749while (readKeyboardStatus()) {
750key = bgetc ();
751if (key == 0x4200) f8press = true;
752if ((key & 0xff) == 's' || (key & 0xff) == 'S') spress = true;
753if ((key & 0xff) == 'v' || (key & 0xff) == 'V') vpress = true;
754}
755// If user typed F8, abort quiet mode, and display the menu.
756if (f8press) {
757gBootMode &= ~kBootModeQuiet;
758timeout = 0;
759}
760// If user typed 'v' or 'V', boot in verbose mode.
761if ((gBootMode & kBootModeQuiet) && firstRun && vpress) {
762addBootArg(kVerboseModeFlag);
763}
764// If user typed 's' or 'S', boot in single user mode.
765if ((gBootMode & kBootModeQuiet) && firstRun && spress) {
766addBootArg(kSingleUserModeFlag);
767}
768
769if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
770setCursorPosition(0, 0, 0);
771clearScreenRows(0, kScreenLastRow);
772if (!(gBootMode & kBootModeQuiet)) {
773// Display banner and show hardware info.
774printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
775printf(getVBEInfoString());
776}
777changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
778verbose("Scanning device %x...", gBIOSDev);
779}
780
781// When booting from CD, default to hard drive boot when possible.
782if (isCDROM && firstRun) {
783const char *val;
784char *prompt = NULL;
785char *name = NULL;
786int cnt;
787int optionKey;
788
789if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->chameleonConfig)) {
790prompt = malloc(cnt + 1);
791strncat(prompt, val, cnt);
792} else {
793name = malloc(80);
794getBootVolumeDescription(gBootVolume, name, 79, false);
795prompt = malloc(256);
796sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
797free(name);
798}
799
800if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->chameleonConfig )) {
801// The key specified is a special key.
802} else {
803// Default to F8.
804optionKey = 0x4200;
805}
806
807// If the timeout is zero then it must have been set above due to the
808// early catch of F8 which means the user wants to set boot options
809// which we ought to interpret as meaning he wants to boot the CD.
810if (timeout != 0) {
811key = countdown(prompt, kMenuTopRow, timeout);
812} else {
813key = optionKey;
814}
815
816if (prompt != NULL) {
817free(prompt);
818}
819
820clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
821
822// Hit the option key ?
823if (key == optionKey) {
824gBootMode &= ~kBootModeQuiet;
825timeout = 0;
826} else {
827key = key & 0xFF;
828
829// Try booting hard disk if user pressed 'h'
830if (biosDevIsCDROM(gBIOSDev) && key == 'h') {
831BVRef bvr;
832
833// Look at partitions hosting OS X other than the CD-ROM
834for (bvr = bvChain; bvr; bvr=bvr->next) {
835if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) {
836gBootVolume = bvr;
837}
838}
839}
840goto done;
841}
842}
843
844if (gBootMode & kBootModeQuiet) {
845// No input allowed from user.
846goto done;
847}
848
849if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) {
850// If the user is holding down a modifier key,
851// enter safe mode.
852if ((readKeyboardShiftFlags() & 0x0F) != 0) {
853gBootMode |= kBootModeSafe;
854}
855goto done;
856}
857
858if (gDeviceCount > 0) {
859// Allocate memory for an array of menu items.
860menuItems = malloc(sizeof(MenuItem) * gDeviceCount);
861if (menuItems == NULL) {
862goto done;
863}
864
865// Associate a menu item for each BVRef.
866for (bvr=bvChain, i=gDeviceCount-1, selectIndex=-1; bvr; bvr=bvr->next) {
867if (bvr->visible) {
868getBootVolumeDescription(bvr, menuItems[i].name, sizeof(menuItems[i].name) - 1, true);
869menuItems[i].param = (void *) bvr;
870if (bvr == menuBVR) {
871selectIndex = i;
872}
873i--;
874}
875}
876// Jief : In case the default partition (returned by selectBootVolume) is not in the menu
877if ( selectIndex == -1 )
878{
879selectIndex = 0;
880
881// gDeviceCount is actually > 0, so menuItems[selectIndex] exists
882menuBVR = (BVRef)(menuItems[selectIndex].param);
883// what happen is bvChain is empty ?
884}
885}
886
887if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
888// redraw the background buffer
889gui.logo.draw = true;
890drawBackground();
891gui.devicelist.draw = true;
892gui.redraw = true;
893if (!(gBootMode & kBootModeQuiet)) {
894
895// Check if "Boot Banner"=N switch is present in config file.
896getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig);
897if (showBootBanner) {
898// Display banner and show hardware info.
899gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
900}
901
902// redraw background
903memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
904}
905} else {
906// Clear screen and hide the blinking cursor.
907clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
908changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
909}
910
911nextRow = kMenuTopRow;
912showPrompt = true;
913
914if (gDeviceCount) {
915if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
916printf("Use \30\31 keys to select the startup volume.");
917}
918showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
919nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3;
920}
921
922// Show the boot prompt.
923showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
924showBootPrompt( nextRow, showPrompt );
925
926do {
927if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
928// redraw background
929memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
930// reset cursor co-ords
931gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
932}
933key = getchar();
934updateMenu( key, (void **) &menuBVR );
935newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
936
937if (newShowPrompt != showPrompt) {
938showPrompt = newShowPrompt;
939showBootPrompt( nextRow, showPrompt );
940}
941
942if (showPrompt) {
943updateBootArgs(key);
944}
945
946switch (key) {
947case KEY_ENTER:
948if (gui.menu.draw) {
949key=0;
950break;
951}
952if (*gBootArgs == '?') {
953char * argPtr = gBootArgs;
954
955// Skip the leading "?" character.
956argPtr++;
957getNextArg(&argPtr, booterCommand);
958getNextArg(&argPtr, booterParam);
959
960/*
961* TODO: this needs to be refactored.
962*/
963if (strcmp( booterCommand, "video" ) == 0) {
964if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
965showInfoBox(getVBEInfoString(), getVBEModeInfoString());
966} else {
967printVBEModeInfo();
968}
969} else if ( strcmp( booterCommand, "memory" ) == 0) {
970if (bootArgs->Video.v_display != VGA_TEXT_MODE ) {
971showInfoBox("Memory Map", getMemoryInfoString());
972} else {
973printMemoryInfo();
974}
975} else if (strcmp(booterCommand, "lspci") == 0) {
976lspci();
977} else if (strcmp(booterCommand, "more") == 0) {
978showTextFile(booterParam);
979} else if (strcmp(booterCommand, "rd") == 0) {
980processRAMDiskCommand(&argPtr, booterParam);
981} else if (strcmp(booterCommand, "norescan") == 0) {
982if (gEnableCDROMRescan) {
983gEnableCDROMRescan = false;
984break;
985}
986} else {
987showHelp();
988}
989key = 0;
990showBootPrompt(nextRow, showPrompt);
991break;
992}
993gBootVolume = menuBVR;
994setRootVolume(menuBVR);
995gBIOSDev = menuBVR->biosdev;
996break;
997
998case KEY_ESC:
999clearBootArgs();
1000break;
1001
1002case KEY_F5:
1003// New behavior:
1004// Clear gBootVolume to restart the loop
1005// if the user enabled rescanning the optical drive.
1006// Otherwise boot the default boot volume.
1007if (gEnableCDROMRescan) {
1008gBootVolume = NULL;
1009clearBootArgs();
1010}
1011break;
1012
1013case KEY_F10:
1014gScanSingleDrive = false;
1015scanDisks(gBIOSDev, &bvCount);
1016gBootVolume = NULL;
1017clearBootArgs();
1018break;
1019
1020case KEY_TAB:
1021// New behavior:
1022// Switch between text & graphic interfaces
1023// Only Permitted if started in graphics interface
1024if (useGUI) {
1025if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1026setVideoMode(VGA_TEXT_MODE, 0);
1027
1028setCursorPosition(0, 0, 0);
1029clearScreenRows(0, kScreenLastRow);
1030
1031// Display banner and show hardware info.
1032printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1033printf(getVBEInfoString());
1034
1035clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1036changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1037
1038nextRow = kMenuTopRow;
1039showPrompt = true;
1040
1041if (gDeviceCount) {
1042printf("Use \30\31 keys to select the startup volume.");
1043showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1044nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1045}
1046
1047showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1048showBootPrompt(nextRow, showPrompt);
1049//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1050} else {
1051gui.redraw = true;
1052setVideoMode(GRAPHICS_MODE, 0);
1053updateVRAM();
1054 updateGraphicBootPrompt();
1055}
1056}
1057key = 0;
1058break;
1059
1060default:
1061key = 0;
1062break;
1063}
1064} while (0 == key);
1065
1066done:
1067if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1068clearScreenRows(kMenuTopRow, kScreenLastRow);
1069changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1070}
1071shouldboot = false;
1072gui.menu.draw = false;
1073if (menuItems) {
1074free(menuItems);
1075menuItems = NULL;
1076}
1077execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1078return 0;
1079}
1080
1081//==========================================================================
1082
1083char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1084extern unsigned char chainbootdev;
1085extern unsigned char chainbootflag;
1086
1087bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1088{
1089 int argLen = argName ? strlen(argName) : 0;
1090 int len = argLen + cnt + 1; // +1 to account for space
1091
1092if (argName)
1093len++; // +1 to account for '='
1094
1095 if (len > *cntRemainingP) {
1096 error("Warning: boot arguments too long, truncating\n");
1097 return false;
1098 }
1099
1100 if (argName) {
1101 strncpy( *argP, argName, argLen );
1102 *argP += argLen;
1103 *argP[0] = '=';
1104 (*argP)++;
1105 }
1106
1107 strncpy( *argP, val, cnt );
1108 *argP += cnt;
1109 *argP[0] = ' ';
1110 (*argP)++;
1111
1112 *cntRemainingP -= len;
1113 return true;
1114}
1115
1116//
1117// Returns TRUE if an argument was copied, FALSE otherwise
1118bool
1119processBootArgument(
1120 const char *argName, // The argument to search for
1121 const char *userString, // Typed-in boot arguments
1122 const char *kernelFlags, // Kernel flags from config table
1123 const char *configTable,
1124 char **argP, // Output value
1125 int *cntRemainingP, // Output count
1126 char *foundVal, // found value
1127 int foundValSize // max found value size
1128 )
1129{
1130 const char *val;
1131 int cnt;
1132 bool found = false;
1133
1134 if (getValueForBootKey(userString, argName, &val, &cnt)) {
1135 // Don't copy; these values will be copied at the end of argument processing.
1136 found = true;
1137 } else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1138 // Don't copy; these values will be copied at the end of argument processing.
1139 found = true;
1140 } else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1141 copyArgument(argName, val, cnt, argP, cntRemainingP);
1142 found = true;
1143 }
1144 if (found && foundVal)
1145 strlcpy(foundVal, val, foundValSize);
1146 return found;
1147}
1148
1149// Maximum config table value size
1150#define VALUE_SIZE 2048
1151
1152int
1153processBootOptions()
1154{
1155 const char *cp = gBootArgs;
1156 const char *val = 0;
1157 const char *kernel;
1158 int cnt;
1159 int userCnt;
1160 int cntRemaining;
1161 char *argP;
1162 char *configKernelFlags;
1163 char *valueBuffer;
1164
1165 valueBuffer = malloc(VALUE_SIZE);
1166
1167 skipblanks( &cp );
1168
1169 // Update the unit and partition number.
1170
1171 if ( gBootVolume )
1172 {
1173 if (!( gBootVolume->flags & kBVFlagNativeBoot ))
1174 {
1175 readBootSector( gBootVolume->biosdev, gBootVolume->part_boff,
1176 (void *) 0x7c00 );
1177
1178 //
1179 // Setup edx, and signal intention to chain load the
1180 // foreign booter.
1181 //
1182
1183 chainbootdev = gBootVolume->biosdev;
1184 chainbootflag = 1;
1185
1186 return 1;
1187 }
1188
1189 setRootVolume(gBootVolume);
1190
1191 }
1192 // If no boot volume fail immediately because we're just going to fail
1193 // trying to load the config file anyway.
1194 else
1195 return -1;
1196
1197 // Load config table specified by the user, or use the default.
1198
1199 if (!getValueForBootKey(cp, "config", &val, &cnt)) {
1200 val = 0;
1201 cnt = 0;
1202 }
1203
1204 // Load com.apple.Boot.plist from the selected volume
1205 // and use its contents to override default bootConfig.
1206
1207 loadSystemConfig(&bootInfo->bootConfig);
1208 loadChameleonConfig(&bootInfo->chameleonConfig);
1209
1210 // Use the kernel name specified by the user, or fetch the name
1211 // in the config table, or use the default if not specified.
1212 // Specifying a kernel name on the command line, or specifying
1213 // a non-default kernel name in the config file counts as
1214 // overriding the kernel, which causes the kernelcache not
1215 // to be used.
1216
1217 gOverrideKernel = false;
1218 if (( kernel = extractKernelName((char **)&cp) )) {
1219 strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1220 } else {
1221 if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) ) {
1222 strlcpy( bootInfo->bootFile, val, cnt+1 );
1223 } else {
1224 strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1225 }
1226 }
1227if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) {
1228gOverrideKernel = true;
1229}
1230
1231 cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1232 argP = bootArgs->CommandLine;
1233
1234// Get config kernel flags, if not ignored.
1235 if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1236 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig )) {
1237 val = "";
1238 cnt = 0;
1239 }
1240 configKernelFlags = malloc(cnt + 1);
1241 strlcpy(configKernelFlags, val, cnt + 1);
1242
1243 // boot-uuid can be set either on the command-line or in the config file
1244if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1245 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString))) {
1246 //
1247 // Try an alternate method for getting the root UUID on boot helper partitions.
1248 //
1249 if (gBootVolume->flags & kBVFlagBooter)
1250{
1251// Load the configuration store in the boot helper partition
1252if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1253 {
1254val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1255if (val != NULL)
1256strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1257}
1258 }
1259
1260 // Try to get the volume uuid string
1261if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1262gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1263
1264// If we have the volume uuid add it to the commandline arguments
1265if (strlen(gBootUUIDString))
1266copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1267}
1268
1269 if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1270 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE)) {
1271 cnt = 0;
1272 if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig)) {
1273 valueBuffer[0] = '*';
1274 cnt++;
1275 strlcpy(valueBuffer + 1, val, cnt);
1276 val = valueBuffer;
1277 } else {
1278 if (strlen(gBootUUIDString)) {
1279 val = "*uuid";
1280 cnt = 5;
1281 } else {
1282 // Don't set "rd=.." if there is no boot device key
1283 // and no UUID.
1284 val = "";
1285 cnt = 0;
1286 }
1287 }
1288 if (cnt > 0) {
1289 copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1290 }
1291 strlcpy( gRootDevice, val, (cnt + 1));
1292 }
1293
1294 /*
1295 * Removed. We don't need this anymore.
1296 *
1297 if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1298 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler))) {
1299 getPlatformName(gPlatformName);
1300 copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1301 }
1302 */
1303
1304 if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1305 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt)) {
1306 if (gBootMode & kBootModeSafe) {
1307 copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1308 }
1309 }
1310
1311 // Store the merged kernel flags and boot args.
1312
1313 cnt = strlen(configKernelFlags);
1314 if (cnt) {
1315 if (cnt > cntRemaining) {
1316 error("Warning: boot arguments too long, truncating\n");
1317 cnt = cntRemaining;
1318 }
1319 strncpy(argP, configKernelFlags, cnt);
1320 argP[cnt++] = ' ';
1321 cntRemaining -= cnt;
1322 }
1323 userCnt = strlen(cp);
1324 if (userCnt > cntRemaining) {
1325 error("Warning: boot arguments too long, truncating\n");
1326 userCnt = cntRemaining;
1327 }
1328 strncpy(&argP[cnt], cp, userCnt);
1329 argP[cnt+userCnt] = '\0';
1330
1331if(!shouldboot)
1332{
1333gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1334getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1335
1336gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1337kBootModeSafe : kBootModeNormal;
1338
1339 if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) {
1340 gBootMode = kBootModeSafe;
1341 }
1342}
1343
1344if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1345strlcpy(gMKextName, val, cnt + 1);
1346else
1347gMKextName[0]=0;
1348
1349 free(configKernelFlags);
1350 free(valueBuffer);
1351
1352 return 0;
1353}
1354
1355
1356//==========================================================================
1357// Load the help file and display the file contents on the screen.
1358
1359void showTextBuffer(char *buf_orig, int size)
1360{
1361char*bp;
1362char* buf;
1363intline;
1364intline_offset;
1365intc;
1366
1367if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1368showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1369return;
1370}
1371
1372// Create a copy so that we don't mangle the original
1373buf = malloc(size + 1);
1374memcpy(buf, buf_orig, size);
1375
1376
1377 bp = buf;
1378 while (size-- > 0) {
1379if (*bp == '\n') {
1380*bp = '\0';
1381}
1382bp++;
1383 }
1384 *bp = '\1';
1385 line_offset = 0;
1386
1387 setActiveDisplayPage(1);
1388
1389 while (1) {
1390clearScreenRows(0, 24);
1391setCursorPosition(0, 0, 1);
1392bp = buf;
1393for (line = 0; *bp != '\1' && line < line_offset; line++) {
1394while (*bp != '\0') {
1395bp++;
1396}
1397bp++;
1398}
1399for (line = 0; *bp != '\1' && line < 23; line++) {
1400setCursorPosition(0, line, 1);
1401printf("%s\n", bp);
1402while (*bp != '\0') {
1403bp++;
1404}
1405bp++;
1406}
1407
1408setCursorPosition(0, 23, 1);
1409if (*bp == '\1') {
1410printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1411} else {
1412printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1413}
1414
1415c = getchar();
1416if (c == 'q' || c == 'Q') {
1417break;
1418}
1419if ((c == 'p' || c == 'P') && line_offset > 0) {
1420line_offset -= 23;
1421}
1422if (c == ' ') {
1423if (*bp == '\1') {
1424break;
1425} else {
1426line_offset += 23;
1427}
1428}
1429 }
1430 setActiveDisplayPage(0);
1431}
1432
1433void showHelp(void)
1434{
1435if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1436showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1437} else {
1438showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1439}
1440}
1441
1442void showTextFile(const char * filename)
1443{
1444#define MAX_TEXT_FILE_SIZE 65536
1445char*buf;
1446intfd;
1447intsize;
1448
1449if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0) {
1450printf("\nFile not found: %s\n", filename);
1451sleep(2);
1452return;
1453}
1454
1455 size = file_size(fd);
1456 if (size > MAX_TEXT_FILE_SIZE) {
1457size = MAX_TEXT_FILE_SIZE;
1458}
1459 buf = malloc(size);
1460 read(fd, buf, size);
1461 close(fd);
1462showTextBuffer(buf, size);
1463free(buf);
1464}
1465
1466// This is a very simplistic prompting scheme that just grabs two hex characters
1467// Eventually we need to do something more user-friendly like display a menu
1468// based off of the Multiboot device list
1469
1470int selectAlternateBootDevice(int bootdevice)
1471{
1472int key;
1473int newbootdevice;
1474int digitsI = 0;
1475char *end;
1476char digits[3] = {0,0,0};
1477
1478// We've already printed the current boot device so user knows what it is
1479printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1480printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1481do {
1482key = getchar();
1483switch (ASCII_KEY(key)) {
1484case KEY_BKSP:
1485if (digitsI > 0) {
1486int x, y, t;
1487getCursorPositionAndType(&x, &y, &t);
1488// Assume x is not 0;
1489x--;
1490setCursorPosition(x,y,0); // back up one char
1491// Overwrite with space without moving cursor position
1492putca(' ', 0x07, 1);
1493digitsI--;
1494} else {
1495// TODO: Beep or something
1496}
1497break;
1498
1499case KEY_ENTER:
1500digits[digitsI] = '\0';
1501newbootdevice = strtol(digits, &end, 16);
1502if (end == digits && *end == '\0') {
1503// User entered empty string
1504printf("\nUsing default boot device %x\n", bootdevice);
1505key = 0;
1506} else if(end != digits && *end == '\0') {
1507bootdevice = newbootdevice;
1508printf("\n");
1509key = 0; // We gots da boot device
1510} else {
1511printf("\nCouldn't parse. try again: ");
1512digitsI = 0;
1513}
1514break;
1515
1516default:
1517if (isxdigit(ASCII_KEY(key)) && digitsI < 2) {
1518putchar(ASCII_KEY(key));
1519digits[digitsI++] = ASCII_KEY(key);
1520} else {
1521// TODO: Beep or something
1522}
1523break;
1524};
1525} while (key != 0);
1526
1527return bootdevice;
1528}
1529
1530bool promptForRescanOption(void)
1531{
1532printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1533if (getchar() == KEY_ENTER) {
1534return true;
1535} else {
1536return false;
1537}
1538}
1539

Archive Download this file

Revision: 2313