Chameleon

Chameleon Svn Source Tree

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

Archive Download this file

Revision: HEAD