Chameleon

Chameleon Svn Source Tree

Root/branches/ErmaC/Trunk/i386/boot2/options.c

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

Archive Download this file

Revision: 2045