Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/boot2/options.c

1/*
2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999-2004 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 2.0 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include "boot.h"
26#include "bootstruct.h"
27#include "fdisk.h"
28#include "ramdisk.h"
29#include "gui.h"
30#include "term.h"
31#include "embedded.h"
32#include "pci.h"
33#include "modules.h"
34
35bool showBootBanner = true; //Azi:showinfo
36static bool shouldboot = false;
37
38extern int multiboot_timeout;
39extern int multiboot_timeout_set;
40
41extern BVRef bvChain;
42//extern intmenucount;
43
44extern intgDeviceCount;
45
46intselectIndex = 0;
47MenuItem * menuItems = NULL;
48
49enum {
50 kMenuTopRow = 5,
51 kMenuMaxItems = 10,
52 kScreenLastRow = 24
53};
54
55//==========================================================================
56
57typedef struct {
58 int x;
59 int y;
60 int type;
61} CursorState;
62
63static void changeCursor( int col, int row, int type, CursorState * cs )
64{
65 if (cs) getCursorPositionAndType( &cs->x, &cs->y, &cs->type );
66 setCursorType( type );
67 setCursorPosition( col, row, 0 );
68}
69
70static void moveCursor( int col, int row )
71{
72 setCursorPosition( col, row, 0 );
73}
74
75static void restoreCursor( const CursorState * cs )
76{
77 setCursorPosition( cs->x, cs->y, 0 );
78 setCursorType( cs->type );
79}
80
81//==========================================================================
82
83/* Flush keyboard buffer; returns TRUE if any of the flushed
84 * characters was F8.
85 */
86
87static bool flushKeyboardBuffer(void)
88{
89 bool status = false;
90
91 while ( readKeyboardStatus() ) {
92 if (bgetc() == 0x4200) status = true;
93 }
94 return status;
95}
96
97//==========================================================================
98
99static int countdown( const char * msg, int row, int timeout )
100{
101 unsigned long time;
102 int ch = 0;
103 int col = strlen(msg) + 1;
104
105 flushKeyboardBuffer();
106
107if( bootArgs->Video.v_display == VGA_TEXT_MODE )
108{
109moveCursor( 0, row );
110printf(msg);
111
112} else {
113
114position_t p = pos( gui.screen.width / 2 + 1 , ( gui.devicelist.pos.y + 3 ) + ( ( gui.devicelist.height - gui.devicelist.iconspacing ) / 2 ) );
115
116char dummy[80];
117getBootVolumeDescription( gBootVolume, dummy, sizeof(dummy) - 1, true );
118drawDeviceIcon( gBootVolume, gui.screen.pixmap, p, true );
119drawStrCenteredAt( (char *) msg, &font_small, gui.screen.pixmap, gui.countdown.pos );
120
121// make this screen the new background
122memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
123
124}
125
126int multi_buff = 18 * (timeout);
127 int multi = ++multi_buff;
128
129 int lasttime=0;
130
131 for ( time = time18(), timeout++; timeout > 0; )
132 {
133if( time18() > lasttime)
134{
135multi--;
136lasttime=time18();
137}
138
139 if ( (ch = readKeyboardStatus()) )
140 break;
141
142 // Count can be interrupted by holding down shift,
143 // control or alt key
144 if ( ( readKeyboardShiftFlags() & 0x0F ) != 0 )
145{
146 ch = 1;
147 break;
148 }
149
150 if ( time18() >= time )
151 {
152 time += 18;
153 timeout--;
154
155if( bootArgs->Video.v_display == VGA_TEXT_MODE )
156{
157moveCursor( col, row );
158printf("(%d) ", timeout);
159}
160 }
161
162if( bootArgs->Video.v_display != VGA_TEXT_MODE )
163{
164drawProgressBar( gui.screen.pixmap, 100, gui.progressbar.pos , ( multi * 100 / multi_buff ) );
165gui.redraw = true;
166updateVRAM();
167}
168
169 }
170
171 flushKeyboardBuffer();
172
173 return ch;
174}
175
176//==========================================================================
177
178char gBootArgs[BOOT_STRING_LEN];
179static char * gBootArgsPtr = gBootArgs;
180static char * gBootArgsEnd = gBootArgs + BOOT_STRING_LEN - 1;
181static char booterCommand[BOOT_STRING_LEN];
182static char booterParam[BOOT_STRING_LEN];
183
184static void clearBootArgs(void)
185{
186gBootArgsPtr = gBootArgs;
187memset(gBootArgs, '\0', BOOT_STRING_LEN);
188
189if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
190clearGraphicBootPrompt();
191}
192
193 execute_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
218 clearBootArgs();
219
220if (visible) {
221if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
222if (gEnableCDROMRescan) {
223printf( bootRescanPrompt );
224} else {
225printf( bootPrompt );
226 printf( 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) {
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=0; 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}
873
874if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
875// redraw the background buffer
876gui.logo.draw = true;
877drawBackground();
878gui.devicelist.draw = true;
879gui.redraw = true;
880if (!(gBootMode & kBootModeQuiet)) {
881
882// Check if "Boot Banner"=N switch is present in config file.
883getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->chameleonConfig);
884if (showBootBanner) {
885// Display banner and show hardware info.
886gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
887}
888
889// redraw background
890memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
891}
892} else {
893// Clear screen and hide the blinking cursor.
894clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
895changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
896}
897
898nextRow = kMenuTopRow;
899showPrompt = true;
900
901if (gDeviceCount) {
902if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
903printf("Use \30\31 keys to select the startup volume.");
904}
905showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
906nextRow += MIN( gDeviceCount, kMenuMaxItems ) + 3;
907}
908
909// Show the boot prompt.
910showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
911showBootPrompt( nextRow, showPrompt );
912
913do {
914if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
915// redraw background
916memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
917// reset cursor co-ords
918gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
919}
920key = getchar();
921updateMenu( key, (void **) &menuBVR );
922newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
923
924if (newShowPrompt != showPrompt) {
925showPrompt = newShowPrompt;
926showBootPrompt( nextRow, showPrompt );
927}
928
929if (showPrompt) {
930updateBootArgs(key);
931}
932
933switch (key) {
934case KEY_ENTER:
935if (gui.menu.draw) {
936key=0;
937break;
938}
939if (*gBootArgs == '?') {
940char * argPtr = gBootArgs;
941
942// Skip the leading "?" character.
943argPtr++;
944getNextArg(&argPtr, booterCommand);
945getNextArg(&argPtr, booterParam);
946
947/*
948* TODO: this needs to be refactored.
949*/
950if (strcmp( booterCommand, "video" ) == 0) {
951if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
952showInfoBox(getVBEInfoString(), getVBEModeInfoString());
953} else {
954printVBEModeInfo();
955}
956} else if ( strcmp( booterCommand, "memory" ) == 0) {
957if (bootArgs->Video.v_display != VGA_TEXT_MODE ) {
958showInfoBox("Memory Map", getMemoryInfoString());
959} else {
960printMemoryInfo();
961}
962} else if (strcmp(booterCommand, "lspci") == 0) {
963lspci();
964} else if (strcmp(booterCommand, "more") == 0) {
965showTextFile(booterParam);
966} else if (strcmp(booterCommand, "rd") == 0) {
967processRAMDiskCommand(&argPtr, booterParam);
968} else if (strcmp(booterCommand, "norescan") == 0) {
969if (gEnableCDROMRescan) {
970gEnableCDROMRescan = false;
971break;
972}
973} else {
974showHelp();
975}
976key = 0;
977showBootPrompt(nextRow, showPrompt);
978break;
979}
980gBootVolume = menuBVR;
981setRootVolume(menuBVR);
982gBIOSDev = menuBVR->biosdev;
983break;
984
985case KEY_ESC:
986clearBootArgs();
987break;
988
989case KEY_F5:
990// New behavior:
991// Clear gBootVolume to restart the loop
992// if the user enabled rescanning the optical drive.
993// Otherwise boot the default boot volume.
994if (gEnableCDROMRescan) {
995gBootVolume = NULL;
996clearBootArgs();
997}
998break;
999
1000case KEY_F10:
1001gScanSingleDrive = false;
1002scanDisks(gBIOSDev, &bvCount);
1003gBootVolume = NULL;
1004clearBootArgs();
1005break;
1006
1007case KEY_TAB:
1008// New behavior:
1009// Switch between text & graphic interfaces
1010// Only Permitted if started in graphics interface
1011if (useGUI) {
1012if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1013setVideoMode(VGA_TEXT_MODE, 0);
1014
1015setCursorPosition(0, 0, 0);
1016clearScreenRows(0, kScreenLastRow);
1017
1018// Display banner and show hardware info.
1019printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1020printf(getVBEInfoString());
1021
1022clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1023changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1024
1025nextRow = kMenuTopRow;
1026showPrompt = true;
1027
1028if (gDeviceCount) {
1029printf("Use \30\31 keys to select the startup volume.");
1030showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1031nextRow += MIN(gDeviceCount, kMenuMaxItems) + 3;
1032}
1033
1034showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1035showBootPrompt(nextRow, showPrompt);
1036//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1037} else {
1038gui.redraw = true;
1039setVideoMode(GRAPHICS_MODE, 0);
1040updateVRAM();
1041 updateGraphicBootPrompt();
1042}
1043}
1044key = 0;
1045break;
1046
1047default:
1048key = 0;
1049break;
1050}
1051} while (0 == key);
1052
1053done:
1054if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1055clearScreenRows(kMenuTopRow, kScreenLastRow);
1056changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1057}
1058shouldboot = false;
1059gui.menu.draw = false;
1060if (menuItems) {
1061free(menuItems);
1062menuItems = NULL;
1063}
1064 execute_hook("BootOptions", gBootArgs, gBootArgsPtr, NULL, NULL);
1065return 0;
1066}
1067
1068//==========================================================================
1069
1070char gBootUUIDString[32+4+1] = ""; // UUID of the boot volume e.g. 5EB1869F-C4FA-3502-BDEB-3B8ED5D87292
1071extern unsigned char chainbootdev;
1072extern unsigned char chainbootflag;
1073
1074bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1075{
1076 int argLen = argName ? strlen(argName) : 0;
1077 int len = argLen + cnt + 1; // +1 to account for space
1078
1079if (argName)
1080len++; // +1 to account for '='
1081
1082 if (len > *cntRemainingP) {
1083 error("Warning: boot arguments too long, truncating\n");
1084 return false;
1085 }
1086
1087 if (argName) {
1088 strncpy( *argP, argName, argLen );
1089 *argP += argLen;
1090 *argP[0] = '=';
1091 (*argP)++;
1092 }
1093
1094 strncpy( *argP, val, cnt );
1095 *argP += cnt;
1096 *argP[0] = ' ';
1097 (*argP)++;
1098
1099 *cntRemainingP -= len;
1100 return true;
1101}
1102
1103//
1104// Returns TRUE if an argument was copied, FALSE otherwise
1105bool
1106processBootArgument(
1107 const char *argName, // The argument to search for
1108 const char *userString, // Typed-in boot arguments
1109 const char *kernelFlags, // Kernel flags from config table
1110 const char *configTable,
1111 char **argP, // Output value
1112 int *cntRemainingP, // Output count
1113 char *foundVal, // found value
1114 int foundValSize // max found value size
1115 )
1116{
1117 const char *val;
1118 int cnt;
1119 bool found = false;
1120
1121 if (getValueForBootKey(userString, argName, &val, &cnt)) {
1122 // Don't copy; these values will be copied at the end of argument processing.
1123 found = true;
1124 } else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1125 // Don't copy; these values will be copied at the end of argument processing.
1126 found = true;
1127 } else if (getValueForKey(argName, &val, &cnt, &bootInfo->chameleonConfig)) {
1128 copyArgument(argName, val, cnt, argP, cntRemainingP);
1129 found = true;
1130 }
1131 if (found && foundVal)
1132 strlcpy(foundVal, val, foundValSize);
1133 return found;
1134}
1135
1136// Maximum config table value size
1137#define VALUE_SIZE 2048
1138
1139int
1140processBootOptions()
1141{
1142 const char *cp = gBootArgs;
1143 const char *val = 0;
1144 const char *kernel;
1145 int cnt;
1146 int userCnt;
1147 int cntRemaining;
1148 char *argP;
1149 char *configKernelFlags;
1150 char *valueBuffer;
1151
1152 valueBuffer = malloc(VALUE_SIZE);
1153
1154 skipblanks( &cp );
1155
1156 // Update the unit and partition number.
1157
1158 if ( gBootVolume )
1159 {
1160 if (!( gBootVolume->flags & kBVFlagNativeBoot ))
1161 {
1162 readBootSector( gBootVolume->biosdev, gBootVolume->part_boff,
1163 (void *) 0x7c00 );
1164
1165 //
1166 // Setup edx, and signal intention to chain load the
1167 // foreign booter.
1168 //
1169
1170 chainbootdev = gBootVolume->biosdev;
1171 chainbootflag = 1;
1172
1173 return 1;
1174 }
1175
1176 setRootVolume(gBootVolume);
1177
1178 }
1179 // If no boot volume fail immediately because we're just going to fail
1180 // trying to load the config file anyway.
1181 else
1182 return -1;
1183
1184 // Load config table specified by the user, or use the default.
1185
1186 if (!getValueForBootKey(cp, "config", &val, &cnt)) {
1187 val = 0;
1188 cnt = 0;
1189 }
1190
1191 // Load com.apple.Boot.plist from the selected volume
1192 // and use its contents to override default bootConfig.
1193
1194 loadSystemConfig(&bootInfo->bootConfig);
1195 loadChameleonConfig(&bootInfo->chameleonConfig);
1196
1197 // Use the kernel name specified by the user, or fetch the name
1198 // in the config table, or use the default if not specified.
1199 // Specifying a kernel name on the command line, or specifying
1200 // a non-default kernel name in the config file counts as
1201 // overriding the kernel, which causes the kernelcache not
1202 // to be used.
1203
1204 gOverrideKernel = false;
1205 if (( kernel = extractKernelName((char **)&cp) )) {
1206 strlcpy( bootInfo->bootFile, kernel, sizeof(bootInfo->bootFile) );
1207 } else {
1208 if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) ) {
1209 strlcpy( bootInfo->bootFile, val, cnt+1 );
1210 } else {
1211 strlcpy( bootInfo->bootFile, kDefaultKernel, sizeof(bootInfo->bootFile) );
1212 }
1213 }
1214if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) {
1215gOverrideKernel = true;
1216}
1217
1218 cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1219 argP = bootArgs->CommandLine;
1220
1221// Get config kernel flags, if not ignored.
1222 if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1223 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig )) {
1224 val = "";
1225 cnt = 0;
1226 }
1227 configKernelFlags = malloc(cnt + 1);
1228 strlcpy(configKernelFlags, val, cnt + 1);
1229
1230 // boot-uuid can be set either on the command-line or in the config file
1231if (!processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config,
1232 &argP, &cntRemaining, gBootUUIDString, sizeof(gBootUUIDString))) {
1233 //
1234 // Try an alternate method for getting the root UUID on boot helper partitions.
1235 //
1236 if (gBootVolume->flags & kBVFlagBooter)
1237{
1238// Load the configuration store in the boot helper partition
1239if (loadHelperConfig(&bootInfo->helperConfig) == 0)
1240 {
1241val = getStringForKey(kHelperRootUUIDKey, &bootInfo->helperConfig);
1242if (val != NULL)
1243strlcpy(gBootUUIDString, val, sizeof(gBootUUIDString));
1244}
1245 }
1246
1247 // Try to get the volume uuid string
1248if (!strlen(gBootUUIDString) && gBootVolume->fs_getuuid)
1249gBootVolume->fs_getuuid(gBootVolume, gBootUUIDString);
1250
1251// If we have the volume uuid add it to the commandline arguments
1252if (strlen(gBootUUIDString))
1253copyArgument(kBootUUIDKey, gBootUUIDString, strlen(gBootUUIDString), &argP, &cntRemaining);
1254}
1255
1256 if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config,
1257 &argP, &cntRemaining, gRootDevice, ROOT_DEVICE_SIZE)) {
1258 cnt = 0;
1259 if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->chameleonConfig)) {
1260 valueBuffer[0] = '*';
1261 cnt++;
1262 strlcpy(valueBuffer + 1, val, cnt);
1263 val = valueBuffer;
1264 } else {
1265 if (strlen(gBootUUIDString)) {
1266 val = "*uuid";
1267 cnt = 5;
1268 } else {
1269 // Don't set "rd=.." if there is no boot device key
1270 // and no UUID.
1271 val = "";
1272 cnt = 0;
1273 }
1274 }
1275 if (cnt > 0) {
1276 copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1277 }
1278 strlcpy( gRootDevice, val, (cnt + 1));
1279 }
1280
1281 /*
1282 * Removed. We don't need this anymore.
1283 *
1284 if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config,
1285 &argP, &cntRemaining, gPlatformName, sizeof(gCacheNameAdler))) {
1286 getPlatformName(gPlatformName);
1287 copyArgument(kPlatformKey, gPlatformName, strlen(gPlatformName), &argP, &cntRemaining);
1288 }
1289 */
1290
1291 if (!getValueForBootKey(cp, kSafeModeFlag, &val, &cnt) &&
1292 !getValueForBootKey(configKernelFlags, kSafeModeFlag, &val, &cnt)) {
1293 if (gBootMode & kBootModeSafe) {
1294 copyArgument(0, kSafeModeFlag, strlen(kSafeModeFlag), &argP, &cntRemaining);
1295 }
1296 }
1297
1298 // Store the merged kernel flags and boot args.
1299
1300 cnt = strlen(configKernelFlags);
1301 if (cnt) {
1302 if (cnt > cntRemaining) {
1303 error("Warning: boot arguments too long, truncating\n");
1304 cnt = cntRemaining;
1305 }
1306 strncpy(argP, configKernelFlags, cnt);
1307 argP[cnt++] = ' ';
1308 cntRemaining -= cnt;
1309 }
1310 userCnt = strlen(cp);
1311 if (userCnt > cntRemaining) {
1312 error("Warning: boot arguments too long, truncating\n");
1313 userCnt = cntRemaining;
1314 }
1315 strncpy(&argP[cnt], cp, userCnt);
1316 argP[cnt+userCnt] = '\0';
1317
1318if(!shouldboot)
1319{
1320gVerboseMode = getValueForKey( kVerboseModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ||
1321getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->chameleonConfig );
1322
1323gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) ?
1324kBootModeSafe : kBootModeNormal;
1325
1326 if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->chameleonConfig ) ) {
1327 gBootMode = kBootModeSafe;
1328 }
1329}
1330
1331if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1332strlcpy(gMKextName, val, cnt + 1);
1333else
1334gMKextName[0]=0;
1335
1336 free(configKernelFlags);
1337 free(valueBuffer);
1338
1339 return 0;
1340}
1341
1342
1343//==========================================================================
1344// Load the help file and display the file contents on the screen.
1345
1346void showTextBuffer(char *buf_orig, int size)
1347{
1348char*bp;
1349char* buf;
1350intline;
1351intline_offset;
1352intc;
1353
1354if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1355showInfoBox( "Press q to continue, space for next page.\n",buf_orig );
1356return;
1357}
1358
1359// Create a copy so that we don't mangle the original
1360buf = malloc(size + 1);
1361memcpy(buf, buf_orig, size);
1362
1363
1364 bp = buf;
1365 while (size-- > 0) {
1366if (*bp == '\n') {
1367*bp = '\0';
1368}
1369bp++;
1370 }
1371 *bp = '\1';
1372 line_offset = 0;
1373
1374 setActiveDisplayPage(1);
1375
1376 while (1) {
1377clearScreenRows(0, 24);
1378setCursorPosition(0, 0, 1);
1379bp = buf;
1380for (line = 0; *bp != '\1' && line < line_offset; line++) {
1381while (*bp != '\0') {
1382bp++;
1383}
1384bp++;
1385}
1386for (line = 0; *bp != '\1' && line < 23; line++) {
1387setCursorPosition(0, line, 1);
1388printf("%s\n", bp);
1389while (*bp != '\0') {
1390bp++;
1391}
1392bp++;
1393}
1394
1395setCursorPosition(0, 23, 1);
1396if (*bp == '\1') {
1397printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1398} else {
1399printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1400}
1401
1402c = getchar();
1403if (c == 'q' || c == 'Q') {
1404break;
1405}
1406if ((c == 'p' || c == 'P') && line_offset > 0) {
1407line_offset -= 23;
1408}
1409if (c == ' ') {
1410if (*bp == '\1') {
1411break;
1412} else {
1413line_offset += 23;
1414}
1415}
1416 }
1417 setActiveDisplayPage(0);
1418}
1419
1420void showHelp(void)
1421{
1422if (bootArgs->Video.v_display != VGA_TEXT_MODE) {
1423showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1424} else {
1425showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1426}
1427}
1428
1429void showTextFile(const char * filename)
1430{
1431#define MAX_TEXT_FILE_SIZE 65536
1432char*buf;
1433intfd;
1434intsize;
1435
1436if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0) {
1437printf("\nFile not found: %s\n", filename);
1438sleep(2);
1439return;
1440}
1441
1442 size = file_size(fd);
1443 if (size > MAX_TEXT_FILE_SIZE) {
1444size = MAX_TEXT_FILE_SIZE;
1445}
1446 buf = malloc(size);
1447 read(fd, buf, size);
1448 close(fd);
1449showTextBuffer(buf, size);
1450free(buf);
1451}
1452
1453// This is a very simplistic prompting scheme that just grabs two hex characters
1454// Eventually we need to do something more user-friendly like display a menu
1455// based off of the Multiboot device list
1456
1457int selectAlternateBootDevice(int bootdevice)
1458{
1459int key;
1460int newbootdevice;
1461int digitsI = 0;
1462char *end;
1463char digits[3] = {0,0,0};
1464
1465// We've already printed the current boot device so user knows what it is
1466printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1467printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1468do {
1469key = getchar();
1470switch (ASCII_KEY(key)) {
1471case KEY_BKSP:
1472if (digitsI > 0) {
1473int x, y, t;
1474getCursorPositionAndType(&x, &y, &t);
1475// Assume x is not 0;
1476x--;
1477setCursorPosition(x,y,0); // back up one char
1478// Overwrite with space without moving cursor position
1479putca(' ', 0x07, 1);
1480digitsI--;
1481} else {
1482// TODO: Beep or something
1483}
1484break;
1485
1486case KEY_ENTER:
1487digits[digitsI] = '\0';
1488newbootdevice = strtol(digits, &end, 16);
1489if (end == digits && *end == '\0') {
1490// User entered empty string
1491printf("\nUsing default boot device %x\n", bootdevice);
1492key = 0;
1493} else if(end != digits && *end == '\0') {
1494bootdevice = newbootdevice;
1495printf("\n");
1496key = 0; // We gots da boot device
1497} else {
1498printf("\nCouldn't parse. try again: ");
1499digitsI = 0;
1500}
1501break;
1502
1503default:
1504if (isxdigit(ASCII_KEY(key)) && digitsI < 2) {
1505putchar(ASCII_KEY(key));
1506digits[digitsI++] = ASCII_KEY(key);
1507} else {
1508// TODO: Beep or something
1509}
1510break;
1511};
1512} while (key != 0);
1513
1514return bootdevice;
1515}
1516
1517bool promptForRescanOption(void)
1518{
1519printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1520if (getchar() == KEY_ENTER) {
1521return true;
1522} else {
1523return false;
1524}
1525}
1526

Archive Download this file

Revision: 2168