Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 169