Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 448