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

Archive Download this file

Revision: 394