Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 33