Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 2271