Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: 602