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, 80, 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;
774char *name;
775int cnt;
776int optionKey;
777
778if (getValueForKey(kCDROMPromptKey, &val, &cnt, &bootInfo->bootConfig)) {
779cnt += 1;
780prompt = malloc(cnt);
781strlcpy(prompt, val, cnt);
782} else {
783name = malloc(80);
784getBootVolumeDescription(gBootVolume, name, 80, false);
785prompt = malloc(256);
786sprintf(prompt, "Press any key to start up from %s, or press F8 to enter startup options.", name);
787free(name);
788cnt = 0;
789}
790
791if (getIntForKey( kCDROMOptionKey, &optionKey, &bootInfo->bootConfig )) {
792// The key specified is a special key.
793} else if (getValueForKey( kCDROMOptionKey, &val, &cnt, &bootInfo->bootConfig ) && cnt >= 1) {
794optionKey = val[0];
795} else {
796// Default to F8.
797optionKey = 0x4200;
798}
799
800// If the timeout is zero then it must have been set above due to the
801// early catch of F8 which means the user wants to set boot options
802// which we ought to interpret as meaning he wants to boot the CD.
803if (timeout != 0) {
804key = countdown(prompt, kMenuTopRow, timeout);
805} else {
806key = optionKey;
807}
808
809if (cnt) {
810free(prompt);
811}
812
813clearScreenRows( kMenuTopRow, kMenuTopRow + 2 );
814
815// Hit the option key ?
816if (key == optionKey) {
817gBootMode &= ~kBootModeQuiet;
818timeout = 0;
819} else {
820key = key & 0xFF;
821
822// Try booting hard disk if user pressed 'h'
823if (biosDevIsCDROM(gBIOSDev) && key == 'h') {
824BVRef bvr;
825
826// Look at partitions hosting OS X other than the CD-ROM
827for (bvr = bvChain; bvr; bvr=bvr->next) {
828if ((bvr->flags & kBVFlagSystemVolume) && bvr->biosdev != gBIOSDev) {
829gBootVolume = bvr;
830}
831}
832}
833goto done;
834}
835}
836
837if (gBootMode & kBootModeQuiet) {
838// No input allowed from user.
839goto done;
840}
841
842if (firstRun && timeout > 0 && countdown("Press any key to enter startup options.", kMenuTopRow, timeout) == 0) {
843// If the user is holding down a modifier key,
844// enter safe mode.
845if ((readKeyboardShiftFlags() & 0x0F) != 0) {
846gBootMode |= kBootModeSafe;
847}
848goto done;
849}
850
851if (gDeviceCount) {
852// Allocate memory for an array of menu items.
853menuItems = malloc(sizeof(MenuItem) * gDeviceCount);
854if (menuItems == NULL) {
855goto done;
856}
857
858// Associate a menu item for each BVRef.
859for (bvr=bvChain, i=gDeviceCount-1, selectIndex=0; bvr; bvr=bvr->next) {
860if (bvr->visible) {
861getBootVolumeDescription(bvr, menuItems[i].name, 80, true);
862menuItems[i].param = (void *) bvr;
863if (bvr == menuBVR) {
864selectIndex = i;
865}
866i--;
867}
868}
869}
870
871if (bootArgs->Video.v_display == GRAPHICS_MODE) {
872// redraw the background buffer
873gui.logo.draw = true;
874drawBackground();
875gui.devicelist.draw = true;
876gui.redraw = true;
877if (!(gBootMode & kBootModeQuiet)) {
878bool showBootBanner = true;
879
880// Check if "Boot Banner"=N switch is present in config file.
881getBoolForKey(kBootBannerKey, &showBootBanner, &bootInfo->bootConfig);
882if (showBootBanner) {
883// Display banner and show hardware info.
884gprintf(&gui.screen, bootBanner + 1, (bootInfo->convmem + bootInfo->extmem) / 1024);
885}
886
887// redraw background
888memcpy(gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4);
889}
890} else {
891// Clear screen and hide the blinking cursor.
892clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
893changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
894}
895
896nextRow = kMenuTopRow;
897showPrompt = true;
898
899if (gDeviceCount) {
900if( bootArgs->Video.v_display == VGA_TEXT_MODE ) {
901printf("Use \30\31 keys to select the startup volume.");
902}
903showMenu( menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems );
904nextRow += min( gDeviceCount, kMenuMaxItems ) + 3;
905}
906
907// Show the boot prompt.
908showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
909showBootPrompt( nextRow, showPrompt );
910
911do {
912if (bootArgs->Video.v_display == GRAPHICS_MODE) {
913// redraw background
914memcpy( gui.backbuffer->pixels, gui.screen.pixmap->pixels, gui.backbuffer->width * gui.backbuffer->height * 4 );
915// reset cursor co-ords
916gui.debug.cursor = pos( gui.screen.width - 160 , 10 );
917}
918key = getc();
919updateMenu( key, (void **) &menuBVR );
920newShowPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
921
922if (newShowPrompt != showPrompt) {
923showPrompt = newShowPrompt;
924showBootPrompt( nextRow, showPrompt );
925}
926
927if (showPrompt) {
928updateBootArgs(key);
929}
930
931switch (key) {
932case kReturnKey:
933if (gui.menu.draw) {
934key=0;
935break;
936}
937if (*gBootArgs == '?') {
938char * argPtr = gBootArgs;
939
940// Skip the leading "?" character.
941argPtr++;
942getNextArg(&argPtr, booterCommand);
943getNextArg(&argPtr, booterParam);
944
945/*
946* TODO: this needs to be refactored.
947*/
948if (strcmp( booterCommand, "video" ) == 0) {
949if (bootArgs->Video.v_display == GRAPHICS_MODE) {
950showInfoBox(getVBEInfoString(), getVBEModeInfoString());
951} else {
952printVBEModeInfo();
953}
954} else if ( strcmp( booterCommand, "memory" ) == 0) {
955if (bootArgs->Video.v_display == GRAPHICS_MODE ) {
956showInfoBox("Memory Map", getMemoryInfoString());
957} else {
958printMemoryInfo();
959}
960} else if (strcmp(booterCommand, "lspci") == 0) {
961lspci();
962} else if (strcmp(booterCommand, "more") == 0) {
963showTextFile(booterParam);
964} else if (strcmp(booterCommand, "rd") == 0) {
965processRAMDiskCommand(&argPtr, booterParam);
966} else if (strcmp(booterCommand, "norescan") == 0) {
967if (gEnableCDROMRescan) {
968gEnableCDROMRescan = false;
969break;
970}
971} else {
972showHelp();
973}
974key = 0;
975showBootPrompt(nextRow, showPrompt);
976break;
977}
978gBootVolume = menuBVR;
979setRootVolume(menuBVR);
980gBIOSDev = menuBVR->biosdev;
981break;
982
983case kEscapeKey:
984clearBootArgs();
985break;
986
987case kF5Key:
988// New behavior:
989// Clear gBootVolume to restart the loop
990// if the user enabled rescanning the optical drive.
991// Otherwise boot the default boot volume.
992if (gEnableCDROMRescan) {
993gBootVolume = NULL;
994clearBootArgs();
995}
996break;
997
998case kF10Key:
999gScanSingleDrive = false;
1000scanDisks(gBIOSDev, &bvCount);
1001gBootVolume = NULL;
1002clearBootArgs();
1003break;
1004
1005case kTabKey:
1006// New behavior:
1007// Switch between text & graphic interfaces
1008// Only Permitted if started in graphics interface
1009if (useGUI) {
1010if (bootArgs->Video.v_display == GRAPHICS_MODE) {
1011setVideoMode(VGA_TEXT_MODE, 0);
1012
1013setCursorPosition(0, 0, 0);
1014clearScreenRows(0, kScreenLastRow);
1015
1016// Display banner and show hardware info.
1017printf(bootBanner, (bootInfo->convmem + bootInfo->extmem) / 1024);
1018printf(getVBEInfoString());
1019
1020clearScreenRows(kMenuTopRow, kMenuTopRow + 2);
1021changeCursor(0, kMenuTopRow, kCursorTypeHidden, 0);
1022
1023nextRow = kMenuTopRow;
1024showPrompt = true;
1025
1026if (gDeviceCount) {
1027printf("Use \30\31 keys to select the startup volume.");
1028showMenu(menuItems, gDeviceCount, selectIndex, kMenuTopRow + 2, kMenuMaxItems);
1029nextRow += min(gDeviceCount, kMenuMaxItems) + 3;
1030}
1031
1032showPrompt = (gDeviceCount == 0) || (menuBVR->flags & kBVFlagNativeBoot);
1033showBootPrompt(nextRow, showPrompt);
1034//changeCursor( 0, kMenuTopRow, kCursorTypeUnderline, 0 );
1035} else {
1036gui.redraw = true;
1037setVideoMode(GRAPHICS_MODE, 0);
1038updateVRAM();
1039}
1040}
1041key = 0;
1042break;
1043
1044default:
1045key = 0;
1046break;
1047}
1048} while (0 == key);
1049
1050done:
1051if (bootArgs->Video.v_display == VGA_TEXT_MODE) {
1052clearScreenRows(kMenuTopRow, kScreenLastRow);
1053changeCursor(0, kMenuTopRow, kCursorTypeUnderline, 0);
1054}
1055shouldboot = false;
1056gui.menu.draw = false;
1057if (menuItems) {
1058free(menuItems);
1059menuItems = NULL;
1060}
1061return 0;
1062}
1063
1064//==========================================================================
1065
1066extern unsigned char chainbootdev;
1067extern unsigned char chainbootflag;
1068
1069bool copyArgument(const char *argName, const char *val, int cnt, char **argP, int *cntRemainingP)
1070{
1071 int argLen = argName ? strlen(argName) : 0;
1072 int len = argLen + cnt + 1; // +1 to account for space
1073
1074 if (len > *cntRemainingP) {
1075 error("Warning: boot arguments too long, truncating\n");
1076 return false;
1077 }
1078
1079 if (argName) {
1080 strncpy( *argP, argName, argLen );
1081 *argP += argLen;
1082 *argP[0] = '=';
1083 (*argP)++;
1084 len++; // +1 to account for '='
1085 }
1086 strncpy( *argP, val, cnt );
1087 *argP += cnt;
1088 *argP[0] = ' ';
1089 (*argP)++;
1090
1091 *cntRemainingP -= len;
1092 return true;
1093}
1094
1095//
1096// Returns TRUE if an argument was copied, FALSE otherwise
1097bool
1098processBootArgument(
1099 const char *argName, // The argument to search for
1100 const char *userString, // Typed-in boot arguments
1101 const char *kernelFlags, // Kernel flags from config table
1102 const char *configTable,
1103 char **argP, // Output value
1104 int *cntRemainingP, // Output count
1105 char *foundVal // found value
1106 )
1107{
1108 const char *val;
1109 int cnt;
1110 bool found = false;
1111
1112 if (getValueForBootKey(userString, argName, &val, &cnt)) {
1113 // Don't copy; these values will be copied at the end of argument processing.
1114 found = true;
1115 } else if (getValueForBootKey(kernelFlags, argName, &val, &cnt)) {
1116 // Don't copy; these values will be copied at the end of argument processing.
1117 found = true;
1118 } else if (getValueForKey(argName, &val, &cnt, &bootInfo->bootConfig)) {
1119 copyArgument(argName, val, cnt, argP, cntRemainingP);
1120 found = true;
1121 }
1122 if (found && foundVal) {
1123 strlcpy(foundVal, val, cnt+1);
1124 }
1125 return found;
1126}
1127
1128// Maximum config table value size
1129#define VALUE_SIZE 2048
1130
1131int
1132processBootOptions()
1133{
1134 const char * cp = gBootArgs;
1135 const char * val = 0;
1136 const char * kernel;
1137 int cnt;
1138 int userCnt;
1139 int cntRemaining;
1140 char * argP;
1141 char uuidStr[64];
1142 bool uuidSet = false;
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 // This is not a mandatory opeartion anymore.
1188
1189 loadOverrideConfig(&bootInfo->overrideConfig);
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 strcpy( bootInfo->bootFile, kernel );
1201 gOverrideKernel = true;
1202 } else {
1203 if ( getValueForKey( kKernelNameKey, &val, &cnt, &bootInfo->bootConfig ) ) {
1204 strlcpy( bootInfo->bootFile, val, cnt+1 );
1205 if (strcmp( bootInfo->bootFile, kDefaultKernel ) != 0) {
1206 gOverrideKernel = true;
1207 }
1208 } else {
1209 strcpy( bootInfo->bootFile, kDefaultKernel );
1210 }
1211 }
1212
1213 cntRemaining = BOOT_STRING_LEN - 2; // save 1 for NULL, 1 for space
1214 argP = bootArgs->CommandLine;
1215
1216 // Get config table kernel flags, if not ignored.
1217 if (getValueForBootKey(cp, kIgnoreBootFileFlag, &val, &cnt) ||
1218 !getValueForKey( kKernelFlagsKey, &val, &cnt, &bootInfo->bootConfig )) {
1219 val = "";
1220 cnt = 0;
1221 }
1222 configKernelFlags = malloc(cnt + 1);
1223 strlcpy(configKernelFlags, val, cnt + 1);
1224
1225 if (processBootArgument(kBootUUIDKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, 0)) {
1226 // boot-uuid was set either on the command-line
1227 // or in the config file.
1228 uuidSet = true;
1229 } else {
1230
1231 //
1232 // Try an alternate method for getting the root UUID on boot helper partitions.
1233 //
1234 if (gBootVolume->flags & kBVFlagBooter)
1235 {
1236 if((loadHelperConfig(&bootInfo->helperConfig) == 0)
1237 && getValueForKey(kHelperRootUUIDKey, &val, &cnt, &bootInfo->helperConfig) )
1238 {
1239 getValueForKey(kHelperRootUUIDKey, &val, &cnt, &bootInfo->helperConfig);
1240 copyArgument(kBootUUIDKey, val, cnt, &argP, &cntRemaining);
1241 uuidSet = true;
1242 }
1243 }
1244
1245 if (!uuidSet && gBootVolume->fs_getuuid && gBootVolume->fs_getuuid (gBootVolume, uuidStr) == 0) {
1246 verbose("Setting boot-uuid to: %s\n", uuidStr);
1247 copyArgument(kBootUUIDKey, uuidStr, strlen(uuidStr), &argP, &cntRemaining);
1248 uuidSet = true;
1249 }
1250 }
1251
1252 if (!processBootArgument(kRootDeviceKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gRootDevice)) {
1253 cnt = 0;
1254 if ( getValueForKey( kBootDeviceKey, &val, &cnt, &bootInfo->bootConfig)) {
1255 valueBuffer[0] = '*';
1256 cnt++;
1257 strlcpy(valueBuffer + 1, val, cnt);
1258 val = valueBuffer;
1259 } else {
1260 if (uuidSet) {
1261 val = "*uuid";
1262 cnt = 5;
1263 } else {
1264 // Don't set "rd=.." if there is no boot device key
1265 // and no UUID.
1266 val = "";
1267 cnt = 0;
1268 }
1269 }
1270 if (cnt > 0) {
1271 copyArgument( kRootDeviceKey, val, cnt, &argP, &cntRemaining);
1272 }
1273 strlcpy( gRootDevice, val, (cnt + 1));
1274 }
1275
1276 /*
1277 * Removed. We don't need this anymore.
1278 *
1279 if (!processBootArgument(kPlatformKey, cp, configKernelFlags, bootInfo->config, &argP, &cntRemaining, gPlatformName)) {
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->bootConfig ) ||
1315getValueForKey( kSingleUserModeFlag, &val, &cnt, &bootInfo->bootConfig );
1316
1317gBootMode = ( getValueForKey( kSafeModeFlag, &val, &cnt, &bootInfo->bootConfig ) ) ?
1318kBootModeSafe : kBootModeNormal;
1319
1320 if ( getValueForKey( kIgnoreCachesFlag, &val, &cnt, &bootInfo->bootConfig ) ) {
1321 gBootMode = kBootModeSafe;
1322 }
1323}
1324
1325if ( getValueForKey( kMKextCacheKey, &val, &cnt, &bootInfo->bootConfig ) )
1326{
1327strlcpy(gMKextName, val, cnt + 1);
1328}
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
1340static void showTextBuffer(char *buf, int size)
1341{
1342char*bp;
1343intline;
1344intline_offset;
1345intc;
1346
1347if (bootArgs->Video.v_display == GRAPHICS_MODE) {
1348showInfoBox( "Press q to quit\n",buf );
1349return;
1350}
1351
1352 bp = buf;
1353 while (size-- > 0) {
1354if (*bp == '\n') {
1355*bp = '\0';
1356}
1357bp++;
1358 }
1359 *bp = '\1';
1360 line_offset = 0;
1361
1362 setActiveDisplayPage(1);
1363
1364 while (1) {
1365clearScreenRows(0, 24);
1366setCursorPosition(0, 0, 1);
1367bp = buf;
1368for (line = 0; *bp != '\1' && line < line_offset; line++) {
1369while (*bp != '\0') {
1370bp++;
1371}
1372bp++;
1373}
1374for (line = 0; *bp != '\1' && line < 23; line++) {
1375setCursorPosition(0, line, 1);
1376printf("%s\n", bp);
1377while (*bp != '\0') {
1378bp++;
1379}
1380bp++;
1381}
1382
1383setCursorPosition(0, 23, 1);
1384if (*bp == '\1') {
1385printf("[Type %sq or space to quit viewer]", (line_offset > 0) ? "p for previous page, " : "");
1386} else {
1387printf("[Type %s%sq to quit viewer]", (line_offset > 0) ? "p for previous page, " : "", (*bp != '\1') ? "space for next page, " : "");
1388}
1389
1390c = getc();
1391if (c == 'q' || c == 'Q') {
1392break;
1393}
1394if ((c == 'p' || c == 'P') && line_offset > 0) {
1395line_offset -= 23;
1396}
1397if (c == ' ') {
1398if (*bp == '\1') {
1399break;
1400} else {
1401line_offset += 23;
1402}
1403}
1404 }
1405 setActiveDisplayPage(0);
1406}
1407
1408void showHelp(void)
1409{
1410if (bootArgs->Video.v_display == GRAPHICS_MODE) {
1411showInfoBox("Help. Press q to quit.\n", (char *)BootHelp_txt);
1412} else {
1413showTextBuffer((char *)BootHelp_txt, BootHelp_txt_len);
1414}
1415}
1416
1417void showTextFile(const char * filename)
1418{
1419#define MAX_TEXT_FILE_SIZE 65536
1420char*buf;
1421intfd;
1422intsize;
1423
1424if ((fd = open_bvdev("bt(0,0)", filename, 0)) < 0) {
1425printf("\nFile not found: %s\n", filename);
1426sleep(2);
1427return;
1428}
1429
1430 size = file_size(fd);
1431 if (size > MAX_TEXT_FILE_SIZE) {
1432size = MAX_TEXT_FILE_SIZE;
1433}
1434 buf = malloc(size);
1435 read(fd, buf, size);
1436 close(fd);
1437showTextBuffer(buf, size);
1438free(buf);
1439}
1440
1441// This is a very simplistic prompting scheme that just grabs two hex characters
1442// Eventually we need to do something more user-friendly like display a menu
1443// based off of the Multiboot device list
1444
1445int selectAlternateBootDevice(int bootdevice)
1446{
1447int key;
1448int newbootdevice;
1449int digitsI = 0;
1450char *end;
1451char digits[3] = {0,0,0};
1452
1453// We've already printed the current boot device so user knows what it is
1454printf("Typical boot devices are 80 (First HD), 81 (Second HD)\n");
1455printf("Enter two-digit hexadecimal boot device [%02x]: ", bootdevice);
1456do {
1457key = getc();
1458switch (key & kASCIIKeyMask) {
1459case kBackspaceKey:
1460if (digitsI > 0) {
1461int x, y, t;
1462getCursorPositionAndType(&x, &y, &t);
1463// Assume x is not 0;
1464x--;
1465setCursorPosition(x,y,0); // back up one char
1466// Overwrite with space without moving cursor position
1467putca(' ', 0x07, 1);
1468digitsI--;
1469} else {
1470// TODO: Beep or something
1471}
1472break;
1473
1474case kReturnKey:
1475digits[digitsI] = '\0';
1476newbootdevice = strtol(digits, &end, 16);
1477if (end == digits && *end == '\0') {
1478// User entered empty string
1479printf("\nUsing default boot device %x\n", bootdevice);
1480key = 0;
1481} else if(end != digits && *end == '\0') {
1482bootdevice = newbootdevice;
1483printf("\n");
1484key = 0; // We gots da boot device
1485} else {
1486printf("\nCouldn't parse. try again: ");
1487digitsI = 0;
1488}
1489break;
1490
1491default:
1492if (isxdigit(key & kASCIIKeyMask) && digitsI < 2) {
1493putc(key & kASCIIKeyMask);
1494digits[digitsI++] = key & kASCIIKeyMask;
1495} else {
1496// TODO: Beep or something
1497}
1498break;
1499};
1500} while (key != 0);
1501
1502return bootdevice;
1503}
1504
1505bool promptForRescanOption(void)
1506{
1507printf("\nWould you like to enable media rescan option?\nPress ENTER to enable or any key to skip.\n");
1508if (getc() == kReturnKey) {
1509return true;
1510} else {
1511return false;
1512}
1513}
1514

Archive Download this file

Revision: 432