Chameleon

Chameleon Svn Source Tree

Root/trunk/i386/boot2/options.c

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

Archive Download this file

Revision: 445