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

Archive Download this file

Revision: 2406